相關結論
-
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);
}