1、 JNI DETECTED ERROR IN APPLICATION: native code passing in reference to invalid local reference: 0x21
in call to CallVoidMethod
原来的代码如下:
extern JNIEnv* jniEnv;
jclass TestProvider;
jobject mTestProvider;
jmethodID getTime;
jmethodID sayHello;
int InitProvider(){
if(jniEnv == NULL){
return 0;
}
if(TestProvider == NULL){
TestProvider = (*jniEnv) -> FindClass(jniEnv,"ruoki/com/jnipractice3/TestProvider");
if(TestProvider == NULL){
return -1;
}
}
if(mTestProvider == NULL){
if(GetProviderInstance(TestProvider) !=1){
(*jniEnv) -> DeleteLocalRef(jniEnv,TestProvider);
return -1;
}
}
if(getTime == NULL){
getTime = (*jniEnv) ->GetStaticMethodID(jniEnv,TestProvider,"getTime","()Ljava/lang/String;");
if(getTime == NULL){
(*jniEnv) -> DeleteLocalRef(jniEnv,TestProvider);
(*jniEnv) -> DeleteLocalRef(jniEnv,mTestProvider);
return -2;
}
}
if(sayHello == NULL){
sayHello = (*jniEnv) -> GetMethodID(jniEnv,TestProvider,"sayHello","(Ljava/lang/String;)V");
if(sayHello == NULL){
(*jniEnv) -> DeleteLocalRef(jniEnv,TestProvider);
(*jniEnv) -> DeleteLocalRef(jniEnv,mTestProvider);
(*jniEnv) -> DeleteLocalRef(jniEnv,getTime);
return -3;
}
}
return 1;
}
int GetProviderInstance(jclass obj_class){
if(obj_class == NULL){
return 0;
}
//默认构造函数,不传参数
jmethodID construction_id = (*jniEnv) -> GetMethodID(jniEnv,obj_class,"","()V");
if(construction_id ==0){
return -1;
}
//通过NewObject来创建对象
mTestProvider = (*jniEnv) -> NewObject(jniEnv,obj_class,construction_id);
if( mTestProvider == NULL){
return -2;
}
return 1;
}
void GetTime(){
if(TestProvider == NULL || getTime == NULL){
int result = InitProvider();
if(result !=1){
return;
}
jstring jstr = NULL;
char* cstr = NULL;
jstr = (*jniEnv) -> CallStaticObjectMethod(jniEnv,TestProvider,getTime);
cstr = (char*)(*jniEnv) -> GetStringUTFChars(jniEnv,jstr,0);
(*jniEnv) -> ReleaseStringUTFChars(jniEnv,jstr,cstr);
(*jniEnv) -> DeleteLocalRef(jniEnv,jstr);
}
void SayHello(){
if(TestProvider == NULL || mTestProvider == NULL || sayHello == NULL){
int result = InitProvider();
if(result !=1){
return;
}
}
jstring jstrMSG = NULL;
jstrMSG = (*jniEnv) -> NewStringUTF(jniEnv,"Hi,I'm From C");
(*jniEnv) -> CallVoidMethod(jniEnv,mTestProvider,sayHello,jstrMSG);
(*jniEnv) -> DeleteLocalRef(jniEnv,jstrMSG);
}
其中GetTime和sayHello由Java调用:
void Java_ruoki_com_jnipractice3_MainActivity_getTime(JNIEnv* env,jobject obj){
if(jniEnv == NULL){
jniEnv = env;
}
GetTime();
}
void Java_ruoki_com_jnipractice3_MainActivity_sayHello(JNIEnv* env,jobject obj){
if(jniEnv == NULL){
jniEnv = env;
}
SayHello();
}
这么写最终导致不管是调用GetTime还是SayHello,调用第二次程序直接crash,日志如下:
JNI DETECTED ERROR IN APPLICATION: native code passing in reference to invalid local reference: 0x21
in call to CallVoidMethod
JNI DETECTED ERROR IN APPLICATION: jclass is an invalid local reference: 0x20001d (0xdead4321) in call to CallStaticObjectMethod
JNI DETECTED ERROR IN APPLICATION: use of deleted local reference 0x19
这个错误显示是CallVoidMethod的参数非法引用,网上找了一些相关问题的帖子,结合本地代码,发现最有可能的就是线程间不能直接传递JNIEnv和jobject这类线程专属属性值导致,要知道JavaVM是属于java进程的,每个进程只有一个JavaVM,而这个JavaVM可以被多线程共享,但是JNIEnv和jobject是属于线程私有的,不能共享,所以:局部引用只在创建它们的线程中有效,跨线程使用是被禁止的。不要在一个线程中创建局部引用并存储到全局引用中,然后到另外一个线程去使用!
这里,我们将上面加粗的代码替换成以下的代码即可:
创建TestProvider 全局引用:
jclass tmpTestProvider = (*jniEnv) -> FindClass(jniEnv,"ruoki/com/jnipractice3/TestProvider");
if(tmpTestProvider == NULL){
return -1;
}
TestProvider = (jclass)(*jniEnv)->NewGlobalRef(jniEnv,tmpTestProvider);
(*jniEnv) -> DeleteLocalRef(jniEnv,tmpTestProvider);
创建mTestProvider 全局引用:
jobject tempTestProviderObject = (*jniEnv) -> NewObject(jniEnv,obj_class,construction_id);
if(tempTestProviderObject == NULL){
return -2;
}
mTestProvider = (jobject)(*jniEnv)->NewGlobalRef(jniEnv,tempTestProviderObject);
(*jniEnv) -> DeleteLocalRef(jniEnv,tempTestProviderObject);
后记:
其实有点不懂:为什么getTime和sayHello这两个引用没有问题呢?由于刚学习jni,菜鸟一枚,如有大神路过,烦请解疑,不胜感激!
2、comparison of array 'num' equal to a null pointer is always false
错误日志为:
解决方案:
将一下语句
if(num == NULL){ return NULL; }
换成:
if(num[0] =='\0'){
return NULL;
}
即可。