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裡面將這些數值打印出來。
- publicclass JniTest extends Activity {
- /** Called when the activity is first created. */
- @Override
- publicvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- JNI j = new JNI();
- j.write();
- }
- public JniTest()
- {
- Log.i("TEST","JniTest Constructor");
- }
- publicint sayHelloFromJava(String str, int index1, int index2, int[] intArray)
- {
- Log.i("TEST", str + " But I am show in java");
- Log.i("TEST", "index1 = " + index1 +
- int javaIndex = 5;
- for(int i = 0; i < intArray.length; ++i)
- {
- Log.i("TEST", "intArray[i] = " + intArray[i]);
- }
- return javaIndex;
- }
- }
- publicclass JNI {
- static
- {
- System.loadLibrary("myjni");
- }
- publicnativevoid write();
- }
然後是C++裡面的程式碼
[cpp] view plaincopyprint?- JNIEXPORT void JNICALL Java_cc_androidos_jni_JNI_write
- (JNIEnv *env, jobject j) {
- LOGI("calltest");
- jstring str = NULL;
- jclass clz = env->FindClass("cc/androidos/jni/JniTest");
- //獲取clz的建構函式並生成一個物件
- jmethodID ctor = env->GetMethodID(clz, "<init>", "()V");
- jobject obj = env->NewObject(clz, ctor);
- // 如果是陣列型別,則在型別前加[,如整形陣列int[] intArray,則對應型別為[I,整形陣列String[] strArray對應為[Ljava/lang/String;
- jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I");
- if (mid)
- {
- LOGI("mid is get");
- jstring str1 = env->NewStringUTF("I am Native");
- jint index1 = 10;
- jint index2 = 12;
- //env->CallVoidMethod(obj, mid, str1, index1, index2);
- // 陣列型別轉換 testIntArray能不能不申請記憶體空間
- jintArray testIntArray = env->NewIntArray(10);
- jint *test = new jint[10];
- for(int i = 0; i < 10; ++i)
- {
- *(test+i) = i + 100;
- }
- env->SetIntArrayRegion(testIntArray, 0, 10, test);
- jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray);
- LOGI("javaIndex = %d", javaIndex);
- delete[] test;
- test = NULL;
- }
- }
通過這個例子我們基本上就可以瞭解C++層是如何回撥Java函式的了。另外,這裡還有一個小技巧,如果你不知道你Java層的在C++中的型別是什麼,你可以native方法中將這個型別寫進去,然後用javah方法生成.h檔案,只要檢視.h檔案的對應的型別註釋就可以知道結果了。例如:我們想知道String、整形陣列對應的型別怎麼寫,我們在native中加入一個public native void type(String str, int[] arrayInt)方法
[java] view plaincopyprint?- publicclass JNI {
- static
- {
- System.loadLibrary("myjni");
- }
- publicnativevoid write();
- publicnativevoid type(String str, int[] arrayInt);
- }
然後生成對應的.h檔案:
[cpp] view plaincopyprint?- /*
- * Class: cc_androidos_jni_JNI
- * Method: type
- * Signature: (Ljava/lang/String;[I)V
- */
- JNIEXPORT void JNICALL Java_cc_androidos_jni_JNI_type
- (JNIEnv *, jobject, jstring, jintArray);
我們注意看註釋中的“Signature: (Ljava/lang/String;[I)V”,其中Ljava/lang/String;Ljava/lang/String;就是String的型別(注意分號不能丟),[I則是整形陣列對應的型別。