2012年9月21日金曜日

JNI Examples for Android

[*]Java interface

We start by defining a Java class JNIExampleInterface, which will provide the interface to calling the native functions, defined in a native (C++) library. The native functions corresponding to Java functions will need to have matching call signatures (i.e. the count and types of the arguments, as well as return type). The easiest way to get the correct function signatures in the native library is to first write down their Java prototypes, and then use the javah tool to generate the native JNI header with native function prototypes. These can be cut and pasted into the C++ file for implementation.

The Java functions which are backed by the corresponding native functions are declared in a usual way, adding a native qualifier. We also want to demonstrate how we could do the callbacks, i.e. calling the Java code from native code. That leads to the following high-level view of our interface class:

<JNIExampleInterface.java>=  package org.wooyd.android.JNIExample;    import android.os.Handler;  import android.os.Bundle;  import android.os.Message;  import org.wooyd.android.JNIExample.Data;    public class JNIExampleInterface {      static Handler h;      <Example constructors>      <Example native functions>      <Example callback>  }  

One valid question about this definition is why we need a Handler class attribute. It turns out that it will come in handy in situations, when the native library wants to pass some information to the Java process through a callback. If the callback will be called by a native thread (for extended discussion see "Calling Java functions" section [->]), and then will try to modify the application's user interface (UI) in any way, an exception will be thrown, as Android only allows the thread which created the UI (the UI thread) to modify it. To overcome this problem we are going to use the message-passing interface provided by Handler to dispatch the data received by a callback to the UI thread, and allow it to do the UI modifications. In order for this to work, we are going to accept a Handler instance as an argument for non-trivial constructor (reasons for keeping trivial one will become apparent later), and save it in a class attribute, and that's pretty much the only task for the constructor:

<Example constructors>= (<-U)      public JNIExampleInterface() {}      public JNIExampleInterface(Handler h) {          this.h = h;      }  

To illustrate various argument-passing techniques, we define three native functions:

  • callVoid(): takes no arguments and returns nothing;
  • getNewData(): takes two arguments and constructs a new class instance using them;
  • getDataString(): extracts a value from an object, which is passed as an argument.

<Example native functions>= (<-U)      public static native void callVoid();      public static native Data getNewData(int i, String s);      public static native String getDataString(Data d);  

The callback will receive a string as an argument, and dispatch it to the Handler instance recorded in the constructor, after wrapping it in a Bundle:

<Example callback>= (<-U)      public static void callBack(String s) {          Bundle b = new Bundle();          b.putString("callback_string", s);          Message m = Message.obtain();          m.setData(b);          m.setTarget(h);          m.sendToTarget();      }  

We also need a definition of a dummy Data class, used purely for illustrative purposes:

<Data.java>=  package org.wooyd.android.JNIExample;    public class Data {      public int i;      public String s;      public Data() {}      public Data(int i, String s) {          this.i = i;          this.s = s;      }  }  

After the source files Data.java and JNIExampleInterface.java are compiled, we can generate the JNI header file, containing the prototypes of the native functions, corresponding to their Java counterparts:

$ javac -classpath /path/to/sdk/android.jar \          org/wooyd/android/JNIExample/*.java  $ javah -classpath . org.wooyd.android.JNIExample.JNIExampleInterface  

Native library implementation

At a high level, the Java library (consisting, in this case, of a single source file JNIExample.cpp) will look like that:

<JNIExample.cpp>=  <JNI includes>  <Miscellaneous includes>  <Global variables>  #ifdef __cplusplus  extern "C" {  

0 件のコメント:

コメントを投稿