JNI-异步线程中执行java方法

仅做记录 2023.1.30

可能有点乱。

可以去看看github中Google提供的NDK样例:https://github.com/android/ndk-samples

在某些时候,有一些JNI操作需要在另一个线程中执行耗时操作,
耗时操作完成后再执行java层中的回调函数,通知操作完成。接着java层进行数据更新。

查阅资料后查询到如下方法:
1.将javaVm保存为全局对象,在执行的方法中用env->GetJavaVM(&javaVm);或者JNI_OnLoad方法中javaVm = vm保存javaVm对象
2.在其他线程中用javaVm获取JNIEnv对象,使用方法附加到当前线程当中,javaVm->AttachCurrentThread,记得线程执行结束后javaVm->DetachCurrentThread();释放。
3.用AttachCurrentThread后,得到env对象,这时就可以查找实例对象中的方法。

使用场景:(非完整代码,仅做流程参考。)
流程:

=》Activity 执行Native方法
=> A.callNativiFunction
=> Native 创建线程
=> Native 线程执行结束后执行 A.callbackFun回调

//  java
class A{

    native void callNativiFunction();

    void callbackFun(){
        //do something
    }
}

class Activity{
    void init(){
         A a=new A();
         a.callNativiFunction();
    }
}

//JNI

void thread1(){
    JavaVm->AttachCurrentThread(&env)
    jmetoodId = env->FindMethodID("callbackFun")
    env->callVoidMethod(jmetoodId);
    javaVm->DetachCurrentThread();
}

以上方法可以在对象A实例化后执行回调方法,但是,如果该方法是静态方法,那么在线程中执行 env->GetStaticMethodID 后会出现找不到方法的错误。

那静态方法该如何执行呢?

将jclass保存全局引用

关键代码
classA = (jclass) jniEnv->NewGlobalRef(tmpClass);

主要是在代码执行后或者在JNI_OnLoad方法中,先用 FindClass 查找到对应的类,然后再将该jclass保存为全局引用对象。这样即可解决。

该方法不确定会不会出现问题,使用请谨慎

#include <jni.h>
#include <string>
#include <iostream>
#include <thread>
#include <android/log.h>
using namespace std;

JavaVM *javaVm = nullptr;
jobject jobj = nullptr;
jmethodID jCallback = nullptr;

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "Tag", __VA_ARGS__)、

/**
 * 线程要执行的方法
 */
void exf(){
    JNIEnv *env;
    if (javaVm == nullptr) {
        return;
    }
    javaVm->AttachCurrentThread(&env, nullptr);
    for (int i = 0; i <= 100; i++) {
        std::this_thread::sleep_for(chrono::seconds (1));
        const char *strs=to_string(i).c_str();
        jstring str=env->NewStringUTF(strs);
        LOGE("Thread:%s",strs);
        env->CallVoidMethod(jobj, jCallback, str);
    }
    javaVm->DetachCurrentThread();
}

/**
 * JNI功能接口
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jnidemo_JNIDemo_startThreadCall(JNIEnv *env, jobject thiz) {
    jobj = env->NewGlobalRef(thiz);
    jclass clas = env->GetObjectClass(thiz);
    jCallback = env->GetMethodID(clas, "nativeCallback", "(Ljava/lang/String;)V");
    env->GetJavaVM(&javaVm);//方式一获取JavaVM对象
    thread th(exf);//新建一个线程
    th.detach();//分离线程,使用join会导致当前的线程阻塞,等待执行结束,耗时操作会导致应用无响应
//    th.join();
}


/* 方式二保存javaVM 为全局对象
jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved){
    javaVm = vm;
    return JNI_VERSION_1_6;
}*/

为了能够调用静态方法,应该将jclass设置为全局引用对象,参考如下代码:

关键点在于将用NewGlobalRef将jclass设置为全局引用


/* 方式二保存javaVM 为全局对象*/
jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved){
    javaVm = vm;
    JNIEnv *jniEnv;
    javaVm->GetEnv((void **) (&jniEnv), JNI_VERSION_1_6);
    jclass tmpClass = jniEnv->FindClass("com/eswd/devicespaceanalysis/FileJNIInterface");
    fClass = (jclass) jniEnv->NewGlobalRef(tmpClass);
    return JNI_VERSION_1_6;
}

或者

extern "C"
JNIEXPORT void JNICALL
Java_com_eswd_devicespaceanalysis_FileJNIInterface_testStaticFunction(JNIEnv *env, jobject thiz) {
    jclass tmpClass = env->FindClass("com/eswd/devicespaceanalysis/FileJNIInterface");
    fClass = (jclass) env->NewGlobalRef(tmpClass);
    env->GetJavaVM(&javaVm);
    thread t2(t1);
    t2.detach();
}

posted @ 2023-01-30 12:00  犯二的二  阅读(307)  评论(0编辑  收藏  举报