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();
}