https://blog.csdn.net/epeaktop/article/details/73849270

最近遇到一个JNI的问题,同一套代码在Android4.4版本前的设备上运行是OK的,但是在Android5.0之后的设备上就会崩溃,查看logcat发现报JNI DETECTED ERROR IN APPLICATION错误。

  • 第一个错误
    JNI DETECTED ERROR IN APPLICATION: calling static method xxxx with CallxxxxMethodV in call to CallxxxxMethodV

有过JNI编程经验的就会知道,调用方法分static和非static两种,分别会用到

GetStaticMethodID                                    GetMethodID    

CallStatic<type>Method                            Call<type>Method

其中指Int, Long, Char等类型
问题的原因就是编写JNI代码的同事混用了这两种方式,先用了GetStaticMethodID,然后又用的非static的CallIntMethod方法,Android4.4之前版本JNI检查机制没有Android5.0之后的版本严格,所以没有报错,程序也不会崩溃,但正确的方式应该是GetMethodID+CallMethod,GetStaticMethodID+CallStaticMethod,这个问题解决后又接着又报出了下面的错误。

  • 第二个错误
    JNI DETECTED ERROR IN APPLICATION: native code passing in reference to invalid stack indirect reference table or invalid reference: 0xfff5f1b0 in call to CallVoidMethod

这个错误显示是CallVoidMethod的参数非法引用,网上找了一些相关问题的帖子,结合本地代码,发现最有可能的就是线程间不能直接传递JNIEnv和jobject这类线程专属属性值导致,要知道JavaVM是属于Java进程的,每个进程只有一个JavaVM,而这个JavaVM可以被多线程共享,但是JNIEnv和jobject是属于线程私有的,不能共享,那有什么办法解决呢?这里我们不讲JNIEnv的线程间传递,有兴趣的网上可以找到相关帖子,而jobject可以用全局引用的方式在多线程间使用,举个简单的例子:

#Include <jni.h>  
#include <pthread.h>  
pthread_t pthread;  
jobject object;  
JavaVM* jvm;  
void *run(void *arg)  
{  
    JNIEnv * jenv;  
    jvm->AttachCurrentThread(&jenv, NULL);  
    jenv->CallVoidMethod(object, jnv->GetMethodID(jnv->GetObjectClass(object), "foo", "()V"));  
    jenv->DeleteGlobalRef(object);  
    jvm->DetachCurrentThread();  
    return NULL;  
}    

void Java_com_program_Initialize(JNIEnv*jenv, jobject caller)  
{   /*要想在新线程中使用对象caller,就必须以全局引用方式保存,否则caller只是局部引用,本方法返回后就会销毁*/  
    object = jnv->NewGlobalRef(caller);   
    jenv->GetJavaVM(&jvm);  
    pthread_create(&thread, NULL, run, NULL);  
}  

再回到前面的问题,按照这个线索,我在本地JNI代码里发现在pthread_create创建新线程之前,仅仅将jobject保存在了一个全局变量里面,而没有使用全局引用,以上面的代码为例,即本地JNI代码里进行了类似object = caller;的赋值,这显然是没有用的,一旦函数返回,caller就会被GC回收销毁,object指向的就是一个非法地址,最终导致上面的JNI错误。

typescript 源码分析

https://www.cnblogs.com/xuld/p/12180913.html

阅读全文

flutter_gen config

const configDefaultYamlContent = ''' name: UNKNOWN flutter_gen: Optional output: lib/gen/ Optional line_length: 80 Optional parse_metadata: false ...

阅读全文

redis 函数的含义

您提供的函数列表是一个 Redis 客户端 API 的部分实现,通常用于与 Redis 数据库进行交互。以下是这些函数的简要解释,按类别进行分类以帮助理解其功能和用...

阅读全文

欢迎留言