在实际项目中,经常需要在Native层创建新的线程处理一些耗时操作,然后将结果回调给java层.如果按照普通的方式,直接获取MethodID,然后新线程中调用CallxxxMethod(),这样肯定是行不通的.当你看到这篇文章时,相信你已经 踩到这个坑了.下面将介绍如何在Native层线程中回调java方法.
Java代码
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native");
}
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
tv = (TextView) findViewById(R.id.sample_text);
tv.setText("Native Update: 0");
}
@Override
protected void onStart() {
super.onStart();
createNativeThread();
}
@Override
protected void onStop() {
super.onStop();
releaseNativeThread();
}
//在jni层创建一个线程
public native void createNativeThread();
//jni层线程退出释放资源
public native void releaseNativeThread();
//该函数会被jni创建的新线程回调
public void callbackFromJNI(int arg){
final int i = arg;
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("Native Update: "+ i);
}
});
Log.d("test",String.format("arg:%d",arg));
}
}
C代码:
#include <pthread.h>
#include "fflog.h"
//记录类相关的信息
typedef struct ClassInfo {
JavaVM *jvm; //保存java虚拟机,这是在新线程中能够回调到java方法的最重要的参数.
jobject obj; //保存java对象
jmethodID callbackMethodId; //保存methodID
}ClassInfo;
//定义一个全局的ClassInfo
ClassInfo gClassInfo = {0};
//该函数用来在线程中调用, callback函数用来调用java方法
void callback(int i)
{
JNIEnv *env;
//通过jvm的接口,attach到当前线程,同时该函数还会获取到所需的env变量.
(*gClassInfo.jvm)->AttachCurrentThread(gClassInfo.jvm, &env, NULL);
//回调java方法
(*env)->CallVoidMethod(env, gClassInfo.obj, gClassInfo.callbackMethodId, i);
//调用完之后,detach.
(*gClassInfo.jvm)->DetachCurrentThread(gClassInfo.jvm);
}
//线程工作函数
int quit;
//用来退出线程循环,实际项目,尽量不要使用全局变量
void* work(void *arg) {
LOGFD("native thread begin...");
int i = 0;
quit = 0;
while(1) {
if(quit) {
break;
}
i++;
callback(i);
//sleep 一秒表示耗时操作.实际使用时添加需要处理代码.
sleep(1);
}
LOGFD("native thread exit...");
}
JNIEXPORT void JNICALLJava_com_example_nativethreadcallbackdemo_MainActivity_createNativeThread(JNIEnv *env, jobject instance)
{
BEGIN
/** 说明:在jni层如果有多线程,实际上JNIEnv(jni环境变量)是不能够在多线程中共用的, env只能在当前线程有效,
* 但是JavaVM可以,JavaVM指Java虚拟机,这个变量是进程可共用的.所以要想在其他线程中回调java方法,需要保存的是jvm.
*/
(*env)->GetJavaVM(env, &gClassInfo.jvm);
jclass cls = (*env)->FindClass(env, "com/example/nativethreadcallbackdemo/MainActivity");
CHECK_NULL(cls)
//获取MethodID
gClassInfo.callbackMethodId = (*env)->GetMethodID(env, cls, "callbackFromJNI", "(I)V");
CHECK_NULL(gClassInfo.callbackMethodId)
/** 说明:为了能够在其它线程得到java的对象,必须要instance转化为全局对象,这样在其它线程才能得到当前java对象的索引.
* 否则在其它线程要用到当前java对象时,会出现无效引用的错误.
*/
gClassInfo.obj = (*env)->NewGlobalRef(env, instance);
CHECK_NULL(gClassInfo.obj)
//创建一个线程
pthread_t pid;
pthread_create(&pid, NULL, work, NULL);
END
}
JNIEXPORT void JNICALLJava_com_example_nativethreadcallbackdemo_MainActivity_releaseNativeThread(JNIEnv *env, jobject instance)
{
quit = 1;
(*env)->DeleteGlobalRef(env, gClassInfo.obj);
gClassInfo.obj = NULL;
}
http://download.csdn.NET/detail/yuzhihui170/9799272