相关结论
-
JNIEnv
无法跨线程, 可以跨函数. -
jobject
无法跨线程, 无法跨函数. -
JavaVM
可以跨线程,跨行数.
在实际开发中, 很有可能在 JNI 中做一些耗时操作, 就需要在 JNI 中用到子线程. 下面用一个栗子🌰来学习.
Java 代码
public class MainActivity extends AppCompatActivity {
// Used to load the 'myapplication' library on application startup.
static {
System.loadLibrary("myapplication");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
//JNI 调用
nativeThreadInvokeJava();
}
//在 JNI 中耗时操作完毕后,调用 java 层的此方法
public void updateUI(){
if(Looper.myLooper() == Looper.getMainLooper()) {
Toast.makeText(this, "主线程更新UI", Toast.LENGTH_SHORT).show();
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "已切换到主线程更新UI", Toast.LENGTH_SHORT).show();
}
});
}
}
//定义 JNI 方法.
native void nativeThreadInvokeJava();
}
JNI 代码
#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h> //导入线程需要的包
JavaVM *_vm;
#define _MY_LOGE(...) __android_log_print(ANDROID_LOG_DEBUG, "JNI", __VA_ARGS__);
struct myContext{
jobject instance;
//JNIEnv *env; 无法在子线程使用 JNIEnv
};
int JNI_OnLoad(JavaVM *vm, void *re){
_vm = vm;
return JNI_VERSION_1_6;
}
//在子线程要执行的方法
void * myThreadTask(void *arges){
//将 native 线程附加到 java 虚拟机, 获得一个当前子线程专用的 jniEnv
JNIEnv * env = nullptr;
jint i = _vm->AttachCurrentThread(&env, nullptr);
if(i != JNI_OK) {
return 0;
}
//执行耗时操作.需要通知 java 状态更新
myContext * context = static_cast<myContext *>(arges);
jclass mainActivityObj = env->GetObjectClass(context->instance);
jmethodID methodId = env->GetMethodID(mainActivityObj, "updateUI", "()V"); //获得方法ID
env->CallVoidMethod(context->instance, methodId);
delete(context);
context = 0;
env->DeleteGlobalRef(context->instance);//删除全局引用
_vm->DetachCurrentThread(); //附加完, 之后完成后,需要分离
return 0;
}
extern "C"
JNIEXPORT void JNICALL Java_com_aaatest_myapplication_MainActivity_nativeThreadInvokeJava(JNIEnv *env, jobject instance) {
myContext *context = new myContext;
//context->env = env; //在子线程中, 无法使用这里传递过去的 JNIEnv; JNIEnv 不能跨线程.
context->instance = env->NewGlobalRef(instance); //将上下文对象传入到结构体
pthread_t pid; //创建一个句柄
pthread_create(&pid, 0, myThreadTask ,context); //启动线程
pthread_join(pid, nullptr);
}