PS:文章不小心写重了,参见另一篇,那篇写的更详细https://blog.csdn.net/rzleilei/article/details/122475387
练习题需求:
点击界面的一个按钮,调用JNI方法,JNI方法中启动JNI线程,JNI线程中不断回调通知安卓进行界面刷新。
初版代码:
安卓代码:
else if (v?.id == R.id.button6) {
//动态注册
var dynamicRegister = DynamicRegister()
// viewHolder.resultText?.text = "$result,$result2"
//新需求:C层启动一个线程,主动通知java层的刷新界面。
dynamicRegister.refresh("ttA")
Log.i("lxllest","tta")
}
fun showMessage(str: String) {
viewHolder.resultText?.text = str
}
JNI代码:
void nativeRefresh(JNIEnv *env, jclass clazz,jstring title){
mEnv = env;
Param param;
param.a = title;
param.b = "1";
param.c = 1;
// int param = 123;
pthread_t pt;
__android_log_print(ANDROID_LOG_INFO,"lxltestjni","nativeRefresh start,tmp.c:%d",param.c);
pthread_create(&pt,NULL,freshAndroidPage,¶m);
sleep(1);//这里加上sleep1秒,就不会释放。但是过了1秒之后param还是会被释放
}
JNI线程
void * freshAndroidPage(void *arg){
Param tmp = *(Param *)arg;
int i=0;
__android_log_print(ANDROID_LOG_INFO,"lxltestjni","freshAndroidPage start,tmp.c:%d",tmp.c);
while (i++<10)
{
__android_log_print(ANDROID_LOG_INFO,"lxltestjni","i:%d", i);
// jclass cls = env->GetObjectClass(activity);
// jmethodID id = env->GetMethodID(cls, "showMessage", "(Ljava/lang/String;)V");
// jstring message = env->NewStringUTF(reinterpret_cast<const char *>(title + ":"[i]));
// env->CallVoidMethod(activity, id, message);
sleep(5);
}
}
排查流程:
问题1:
现象:线程中接收到的传入的参数都是空值。
原因:JNI方法被调用之后,方法内的局部变量会被释放,从而导致线程中指针获取到的值为空。
解决方案:把Param转为全局引用
这样的话,先保证Param中的值可以传入到线程当中了,但是我们的目标是把activity也传递过去。
问题2:传递activity引用到线程。
如果直接传入jobject是不行的,因为jobject也是属于局部引用。跨线程是不能正常使用的。
我们要使用NewGlobalRef进行包装,封装成全局变量才可以正常使用。
最终代码:
java代码不变,只改变JNI代码即可
Param param;
JavaVM *g_vm;
void *freshAndroidPage(void *arg) {
JNIEnv *env;
g_vm->AttachCurrentThread(&env, nullptr);
Param tmp = param;
jobject activity = tmp.activity;
const char *title = env->GetStringUTFChars(tmp.title, JNI_FALSE);
int i = 0;
__android_log_print(ANDROID_LOG_INFO, "lxltestjni",
"freshAndroidPage start,tmp.charTitle:%s,title:%s", tmp.charTitle, title);
while (i++ < 10) {
__android_log_print(ANDROID_LOG_INFO, "lxltestjni", "i:%d,charTitle:%s", i, tmp.charTitle);
jclass cls = env->GetObjectClass(activity);
jmethodID id = env->GetMethodID(cls, "showMessage", "(Ljava/lang/String;)V");
strchr(tmp.charTitle,i);
std::string result = tmp.charTitle;
result.append(std::to_string(i));
jstring message = env->NewStringUTF(result.c_str());
env->CallVoidMethod(activity, id, message);
sleep(5);
}
g_vm->DetachCurrentThread();
}
void nativeRefresh(JNIEnv *env, jclass clazz, jstring title, jobject activity) {
param.title = static_cast<jstring>(env->NewGlobalRef(title));
param.charTitle = env->GetStringUTFChars(title, JNI_FALSE);
param.activity = env->NewGlobalRef(activity);
param.i = 1;
// int param = 123;
pthread_t pt;
__android_log_print(ANDROID_LOG_INFO, "lxltestjni", "nativeRefresh start,tmp.c:%d", param.i);
pthread_create(&pt, NULL, freshAndroidPage, ¶m);//这里的param无用
// sleep(1);
}