其实和c++中在全局函数调用某个对象的成员一样的,这个全局函数就是jni生成函数,而这个类是java中new出来的对象,不过有点不同,如下:
java code
------------------------------------
class JniApi{ public static native void jni_test(); } public class Test: extends Object{ public void callback(){} public void CallJniTest(){JniApi.jni_test();} }
c++ code
-----------------------------------
jvoid xxx_jni_test(JNIEnv *env,jclass jc){ jclass mJc = env->FindClass("xxx/Test"); //上面创建一个jclass 目的在于,传递进来的这个jc所引用的类实际上是JniApi类,我们要调用的java函数是在Test类中 jobject obj = env->AllocObject(mJc ); //创建一个类型为mJc类的实例对象 jmethodID methid = env->GetMethodID(mJc, "callback", "()V"); //获取这个方法在这个类中的id,其实说白了就和linux中container_of宏一样,求这个方法在这个类中的偏移,所以用到的是mJc,而不是obj env->CallVoidMethod(obj,methid); //最后调用方法,这个方法大概执行方式其实就是先得出对象obj的首地址,然后加上这个methid偏移,就能找到这个函数地址并执行了。 }
上面的例子其实是有问题的,
callback函数确实是调用了,但并不是我们在java层中new出来的
Test对象,而是jni中又重新new了一个,所以如果你在java层中用线程去监听这个函数里面的执行情况,那么你就知道有啥效果了。除非这个函数是一个静态函数,对所有实例都只有一份拷贝。
为了避免这种虚假的回调非成员函数,目前我想到的就一种:
1、修改
jni_test参数,增加一个形式参数obj
,即j
ni_test(
Test obj
);
2、在调用这个Jni的时候将this传入,类似于外部函数调用c++类中的方法一样,需要传入这个对象的指针或者引用。
修改后的代码如下:
java code
------------------------------------
class JniApi{ public static native void jni_test(Test obj); } public class Test: extends Object{ public void callback(){} public void CallJniTest(){JniApi.jni_test(this);} }
c++ code
-----------------------------------
jvoid xxx_jni_test(JNIEnv *env,jclass jc, jobject obj){ jclass mJc = env->GetObjectClass(obj) //获取这个实例对象具体属于的类 jmethodID methid = env->GetMethodID(mJc, "callback", "()V"); //得到偏移 env->CallVoidMethod(obj,methid); }
这样,就能保证调用到的是你在java中new出来的对象中的函数了。