我们先引入一段程序:
Java 类,随便打印一些信息吧
package com.dk.test;
public class PrintInfo {
public PrintInfo() {
//ToDo
}
public print(int num, String name, byte[] b) {
Log.i("dk", "num : " + num);
Log.i("dk", "name : " + name);
Log.i("dk", "size : " + b.length);
}
private native boolean parseInfo(string infofile);
static {
System.loadLibrary("test");
}
}
JNI: 演示如何在jni中正确调用java对象的方法:
JNIEXPORT jboolean JNICALL Java_com_dk_test_parseInfo(JNIENV *env, jobject obj, jstring file) {
//一定要去的全局引用,否则会被GC回收,需要记住的是JNI中传入
//的参数及返回的值都是本地引用,也就是离开了当前方法或跨线程
//时这些本地引用会失效
jobject objRef = (jobject)env->NewGlobalRef(obj);
if (objRef == NULL) {
LOGE("error: null pointer exception");
return false;
}
//通过全局引用取得对象的类
jclass objClass = env->GetObjectClass(objRef);
//获取对象的方法
//objclass: 定义了该方法的类
//"print" : 需要在c/c++中调用的方法名
//"(ILjava/lang/String;[B)Z": 方法标签,用来描述方法,不
// 用死记对应关系,只需要找到类对应的class文件执行:
// javap -s 类名
// 即可生成所有该类中方法的描述
// androidstudio class 在
// app/build/intermediates/classes/debug/com/..下面
jmethodId printFuncId = env->GetMethodId(objClass, "print", "(ILjava/lang/String;[B)Z");
jint num = 30;
//但凡jobject的子类jstring,jclass,jarray使用时一定要使
//用全局应用,否则会报控制异常
jstring name = (jstring)env->NewGlobalRef(env->NewStringUTF("dkos"));
//jarray 引用
jbyteArray b = (jbyteArray)env->NewGlobalRef(env->NewByteArray(10));
unsigned char *mail = "dk_mcu@126.com";
jbyte* bcontent = (jbyte*)(mail)
//byte数组的赋值方法
env->SetByteArrayRegion(b, 0, strlen(mail), bcontent);
//调用java类的方法
env->CallBooleanMethod(objRef, printFuncId, num, name, b);
//回收全局引用
env->DeleteGlobalRef(name)
env->DeleteGlobalRef(b);
env->DeleteGlobalRef(objRef);
//jni方法返回
return true;
}
需要注意:假设上面调用java方法,或引用jobject对象,或是传递参数给java方法时,jobject对象若不全局引用常见的错误为:
E/dalvikvm( 5302): JNI ERROR (app bug): accessed stale local reference 0x40839e59
总结:
>
1 jni接口中使用java传递的参数或是传递参数,返回参数给java时一定使用全局引用,否则会被GC回收导致程序crash,报空指针异常等错误
2.使用引用时,env->NewGlobalRef(ref) 和env->DeleteGlobalRef(ref)成对使用,防止过多占用内存,因为全局引用GC时不会回收的
3.上面所讲都是针对android ics之后的版本,至于以前的不需要这么做