JNI中创建新的线程回调java方法的技巧

在实际项目中,经常需要在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变量. 
    //回调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

猜你喜欢

转载自blog.csdn.net/yuzhihui170/article/details/68488309