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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值