栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Android jni c++ 线程中调用java中的方法

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 ) {
    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;



        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并放到全局中

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/982325.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号