原理
C代码回调java方法的原理是利用反射的方法调用的。下面先简单介绍在java中利用反射调用方法的做法。
java工程中有一个业务类Utils,里面有一个test方法,在主类的Main方法中通过反射调用test方法。
Utils.java:
public class Utils {
public void test(String s){
System.out.println(s);
}
}
- Demo.java:
public class Demo {
/**
* @param args
*/
public static void main(String[] args) {
//1.0得到字节码对象
Class clazz = Utils.class;
try {
//2.0获取Method对象
Method method = clazz.getDeclaredMethod("test", String.class);
//3.0通过字节码对象创建一个Object
Object object = clazz.newInstance();
//4.0通过对象调用方法
method.invoke(object, "hello");
} catch (Exception e) {
e.printStackTrace();
}
}
}
JNI中C代码回调java空方法
在JNI类中声明一个java空方法和一个本地方法,然后在C中实现调用java空方法的逻辑。
C调用空方法的逻辑:
JNIEXPORT void JNICALL Java_com_zhong_callbackjava_JNI_callbackvoidmethod(
JNIEnv * env, jobject clazz) {
/*1.0获取字节码对象,jclass (*FindClass)(JNIEnv*, const char*);
第二个参数为所要回调java方法所在的类的路径。*/
jclass claz = (*env)->FindClass(env, "com/zhong/callbackjava/JNI");
/*2.0获取Method对象,jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
第二个参数为字节码对象,第三个参数为要反射调用的java方法名,第四个为java签名。*/
jmethodID methodid = (*env)->GetMethodID(env, claz, "helloFromJava", "()V");
/*3.0通过字节码对象创建一个Object,通过字节码创建 java对象(可选) 如果本地方法和要回调的java方法在同一个类里可以直接用 jni传过来的java对象(即clazz)调用创建的Method
4.0通过对象调用方法, void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);*/
(*env)->CallVoidMethod(env, clazz, methodid);
}
这里涉及一个得到java方法签名(即方法的形参)的方式,
JNI中C调用java中的带两个int参数的方法
JNIEXPORT void JNICALL Java_com_zhong_callbackjava_JNI_callbackintmethod(
JNIEnv * env, jobject clazz) {
jclass claz = (*env)->FindClass(env, "com/zhong/callbackjava/JNI");
jmethodID methodid = (*env)->GetMethodID(env, claz, "add", "(II)I");
int result = (*env)->CallIntMethod(env, clazz, methodid, 5, 8);
LOGD("result = %d", result);
}
JNI中C调用java中参数为string的方法
JNIEXPORT void JNICALL Java_com_zhong_callbackjava_JNI_callbackstringmethod(
JNIEnv * env, jobject clazz) {
jclass claz = (*env)->FindClass(env, "com/zhong/callbackjava/JNI");
jmethodID methodid = (*env)->GetMethodID(env, claz, "printString",
"(Ljava/lang/String;)V");
jstring result = (*env)->NewStringUTF(env, "hello form c!");
(*env)->CallVoidMethod(env, clazz, methodid, result);
}
注意:
- 第三步中通过字节码创建java对象时,当回调的方法跟本地方法不在一个类里 需要通过刚创建的字节码对象手动创建一个java对象,再通过这个对象来回调java的方法。需要注意的是 如果创建的是一个activity对象 回调的方法还包含上下文 这个方法行不通!!!会报空指针异常 。解决办法是在要调用的方法所在的类中增加构造方法传入上下文。
jobject obj =(*env)->AllocObject(env,claz);