1. 程式人生 > >Andriod JNI程式設計之C++回撥Java函式

Andriod JNI程式設計之C++回撥Java函式

一般我們NDK程式設計都是Java層呼叫C++的介面,但其實才C++層也可以呼叫Java的函式。實現方法如下:

1、獲取類名:jclass cls = env->FindClass

2、獲取類方法:jmethodID mid = env->GetMethodID

3、獲取類成員變數:fieldID fid=env->GetFieldID

4、生成類物件:jobject obj=env->NewObject (jobect也可以從Java層傳下來)

5、呼叫類成員方法:env->CallXXXMethod(XXX為Java方法的返回值型別)

下面是一個例子:

首先是Java的程式碼,首先生成一個JniTest類,裡面有個sayHelloFromJava的方法,我們要實現的目標是在C++裡面賦值(String str),兩個整形值(int index1,  int index2),一個整形陣列(int[] intArray),然後在Java裡面將這些數值打印出來。

[java] view plaincopyprint?
  1. publicclass JniTest extends Activity {  
  2.     /** Called when the activity is first created. */
  3.     @Override
  4.     publicvoid onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.main);  
  7.         JNI j = new JNI();  
  8.         j.write();  
  9.     }  
  10.     public JniTest()  
  11.     {  
  12.         Log.i("TEST","JniTest Constructor");  
  13.     }  
  14.     publicint sayHelloFromJava(String str, int index1, int index2, int[] intArray)  
  15.     {  
  16.         Log.i("TEST", str + " But I am show in java");  
  17.         Log.i("TEST""index1 = " + index1 + 
    " index2 = " + index2 );  
  18.         int javaIndex = 5;  
  19.         for(int i = 0; i < intArray.length; ++i)  
  20.         {  
  21.             Log.i("TEST""intArray[i] = " + intArray[i]);  
  22.         }  
  23.         return javaIndex;  
  24.     }  
  25. }  
[java] view plaincopyprint?
  1. publicclass JNI {  
  2.     static
  3.     {  
  4.         System.loadLibrary("myjni");  
  5.     }  
  6.     publicnativevoid write();  
  7. }  

然後是C++裡面的程式碼

[cpp] view plaincopyprint?
  1. JNIEXPORT void JNICALL Java_cc_androidos_jni_JNI_write  
  2. (JNIEnv *env, jobject j) {  
  3.     LOGI("calltest");  
  4.     jstring str = NULL;  
  5.     jclass clz = env->FindClass("cc/androidos/jni/JniTest");  
  6.     //獲取clz的建構函式並生成一個物件
  7.     jmethodID ctor = env->GetMethodID(clz, "<init>""()V");  
  8.     jobject obj = env->NewObject(clz, ctor);  
  9.     // 如果是陣列型別,則在型別前加[,如整形陣列int[] intArray,則對應型別為[I,整形陣列String[] strArray對應為[Ljava/lang/String;
  10.     jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava""(Ljava/lang/String;II[I)I");  
  11.     if (mid)  
  12.     {  
  13.         LOGI("mid is get");  
  14.         jstring str1 = env->NewStringUTF("I am Native");  
  15.         jint index1 = 10;  
  16.         jint index2 = 12;  
  17.         //env->CallVoidMethod(obj, mid, str1, index1, index2);
  18.         // 陣列型別轉換 testIntArray能不能不申請記憶體空間
  19.         jintArray testIntArray = env->NewIntArray(10);  
  20.         jint *test = new jint[10];  
  21.         for(int i = 0; i < 10; ++i)  
  22.         {  
  23.             *(test+i) = i + 100;  
  24.         }  
  25.         env->SetIntArrayRegion(testIntArray, 0, 10, test);  
  26.         jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);  
  27.         LOGI("javaIndex = %d", javaIndex);  
  28.         delete[] test;  
  29.         test = NULL;  
  30.     }  
  31. }  

通過這個例子我們基本上就可以瞭解C++層是如何回撥Java函式的了。另外,這裡還有一個小技巧,如果你不知道你Java層的在C++中的型別是什麼,你可以native方法中將這個型別寫進去,然後用javah方法生成.h檔案,只要檢視.h檔案的對應的型別註釋就可以知道結果了。例如:我們想知道String、整形陣列對應的型別怎麼寫,我們在native中加入一個public native void type(String str, int[] arrayInt)方法

[java] view plaincopyprint?
  1. publicclass JNI {  
  2.     static
  3.     {  
  4.         System.loadLibrary("myjni");  
  5.     }  
  6.     publicnativevoid write();  
  7.     publicnativevoid type(String str, int[] arrayInt);  
  8. }  

然後生成對應的.h檔案:

[cpp] view plaincopyprint?
  1. /* 
  2.  * Class:     cc_androidos_jni_JNI 
  3.  * Method:    type 
  4.  * Signature: (Ljava/lang/String;[I)V 
  5.  */
  6. JNIEXPORT void JNICALL Java_cc_androidos_jni_JNI_type  
  7.   (JNIEnv *, jobject, jstring, jintArray);  

我們注意看註釋中的“Signature: (Ljava/lang/String;[I)V”,其中Ljava/lang/String;Ljava/lang/String;就是String的型別(注意分號不能丟),[I則是整形陣列對應的型別。