IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android jni c++ 线程中调用java中的方法 -> 正文阅读

[移动开发]Android jni c++ 线程中调用java中的方法

最近发行从c++中调用Java的方法会出现调用不到的现象,

例如:

我想在c++中调用如下java的方法

public class Test {

      static int num = 0;


    public static  int testPthread(String str){

        num++;
        Log.e("Test","testPthread:"+str);

        return num;

    }
}

我开始的时候是这样实现的

pthread_t thread;


JNIEnv *get_env(int *attach) {
    if (global_jvm == NULL) return NULL;

    *attach = 0;
    JNIEnv *jni_env = NULL;


    int status = global_jvm->GetEnv((void **)&jni_env, JNI_VERSION_1_6);

    LOGD("status:%d  jni_env:%d",status,jni_env);
    if (status == JNI_EDETACHED || jni_env == NULL) {
        LOGD("AttachCurrentThread start");
        status = global_jvm->AttachCurrentThread(&jni_env, NULL);
        LOGD("AttachCurrentThread status:%d",status);
        if (status < 0) {
            jni_env = NULL;
        } else {
            *attach = 1;
        }
    }
    return jni_env;
}

void del_env() {
    global_jvm->DetachCurrentThread();
}

//void get_jvm(JNIEnv *env) {
//    env->GetJavaVM(&global_jvm);
//}



extern "C" JNIEXPORT jstring JNICALL
Java_com_example_pthreaddemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
void * run(void * ch){


//    //调用上层的方法
//    JNIEnv *env;
//    //从全局的JavaVM中获取到环境变量
//    global_jvm->AttachCurrentThread(&env,NULL);
    int attach = 0;
    JNIEnv *env = get_env(&attach);

    jstring jstr_data = env->NewStringUTF((char *)ch);

    jclass clazz = env->FindClass("com/example/pthreaddemo/Test");
    jmethodID methodID = env->GetStaticMethodID(clazz, "testPthread",
                                                "(Ljava/lang/String;)I");

    int result = env->CallStaticIntMethod(myClass, methodID, jstr_data);
    env->DeleteLocalRef(jstr_data);

    LOGD("pthreadTest Result:%d",result);

    if (attach == 1) {
        del_env();
    }

    return 0;

}



extern "C"
JNIEXPORT jint JNICALL
Java_com_example_pthreaddemo_MainActivity_pthreadTest(JNIEnv *env, jobject thiz, jstring src) {
    // TODO: implement pthreadTest()
    //    pthread_t pthread;

    char * ch =   (char *)env->GetStringUTFChars(src,NULL);
    pthread_create(&thread, NULL, run, (void *)ch);
    pthread_detach(thread);


    return 0;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved){
        JNIEnv* env = NULL;
        jint result = JNI_ERR;
        global_jvm = vm;

/*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);
* GetEnv()函数返回的 Jni 环境对每个线程来说是不同的,
* 由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,
* 所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取
*
*/

        if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            return result;
        }



    return JNI_VERSION_1_4;

}

在c++中我使用pthread_create创建了一个线程,然后在线程中使用反射调用Java层的testPthread方法,果不其然然报错了

当调用FIndClass去寻找java层的类的时候,竟然报错找不到对应的类

于是网上百度了一下,发现,如果c++当前线程不是java层创建的线程,那么就会出现FindClass失效的情况,只能在Java创建的线程中去findClass才不会出错,于是将代码改为如下的获取方式

jclass myClass;
jmethodID methodID;
void * run(void * ch){


//    //调用上层的方法
//    JNIEnv *env;
//    //从全局的JavaVM中获取到环境变量
//    global_jvm->AttachCurrentThread(&env,NULL);
    int attach = 0;
    JNIEnv *env = get_env(&attach);

    jstring jstr_data = env->NewStringUTF((char *)ch);

//    jclass clazz = env->FindClass("com/example/pthreaddemo/Test");
//    jmethodID methodID = env->GetStaticMethodID(clazz, "testPthread",
//                                                "(Ljava/lang/String;)I");

    int result = env->CallStaticIntMethod(myClass, methodID, jstr_data);
    env->DeleteLocalRef(jstr_data);

    LOGD("pthreadTest Result:%d",result);

    if (attach == 1) {
        del_env();
    }

    return 0;

}



extern "C"
JNIEXPORT jint JNICALL
Java_com_example_pthreaddemo_MainActivity_pthreadTest(JNIEnv *env, jobject thiz, jstring src) {
    // TODO: implement pthreadTest()
    //    pthread_t pthread;

    char * ch =   (char *)env->GetStringUTFChars(src,NULL);
    myClass = env->FindClass("com/example/pthreaddemo/Test");
    methodID = env->GetStaticMethodID(myClass, "testPthread",
                                  "(Ljava/lang/String;)I");
    pthread_create(&thread, NULL, run, (void *)ch);
    pthread_detach(thread);


    return 0;
}

可以看到我是在创建线程前获取到的jclass的,并且放在了全局变量,也就是在java线程中获取的全局变量,但还是报了如下错误,大概意思是使用了一个被删除的引用

?于是又搜索了一番,发现findClass获取到的jcalss是局部引用,就算放到全局引用也是不起作用的,经过一番查询,终于找到如下解决方案,使用jni的NewGlobalRef 这个方法,将全局变量放到全局里,修改如下

?

jclass myClass;
jmethodID methodID;
void * run(void * ch){


//    //调用上层的方法
//    JNIEnv *env;
//    //从全局的JavaVM中获取到环境变量
//    global_jvm->AttachCurrentThread(&env,NULL);
    int attach = 0;
    JNIEnv *env = get_env(&attach);

    jstring jstr_data = env->NewStringUTF((char *)ch);



    int result = env->CallStaticIntMethod(myClass, methodID, jstr_data);
    env->DeleteLocalRef(jstr_data);
    env->DeleteGlobalRef(myClass);

    LOGD("pthreadTest Result:%d",result);

    if (attach == 1) {
        del_env();
    }

    return 0;

}



extern "C"
JNIEXPORT jint JNICALL
Java_com_example_pthreaddemo_MainActivity_pthreadTest(JNIEnv *env, jobject thiz, jstring src) {
    // TODO: implement pthreadTest()
    //    pthread_t pthread;

    char * ch =   (char *)env->GetStringUTFChars(src,NULL);
    jclass  tmp = env->FindClass("com/example/pthreaddemo/Test");
    methodID = env->GetStaticMethodID(tmp, "testPthread",
                                      "(Ljava/lang/String;)I");
    myClass = (jclass)env->NewGlobalRef(tmp);
    pthread_create(&thread, NULL, run, (void *)ch);
    pthread_detach(thread);


    return 0;
}

问题完美解决,最后总结一下:

1,jni中很多方法生成的对象都是局部引用,如果要放到全局,必须使用NewGlobalRef

2,如果当前线程是c++创建的线程,那么FindClass会有很大的概率找不到对应的java中的类,此时需要在java创建的线程中使用FindClass并放到全局中

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:16:18  更:2022-06-25 18:17:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 3:02:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码