JNI多线程与全局引用

之前的JNI学习文章《JNI异常处理和缓存策略》中有介绍过全局变量,在本文中将派上用用场,直接使用。

本次实战主要是在C层开辟子线程,然后通过访问java类,获取得到UUID,并且打印出来。

具体步骤:

1、创建一个NDK项目,编写native方法 NDKTest.java

public class NDKTest {

	public native static String getStrFromJNI();//测试

	public native void pthread();

	public native void init();

	public native void destroy();

	static {
		System.loadLibrary("myndk");
	}

}
复制代码

init也就是初始化,主要是获取class,通过class获取jmethodID等操作。

pthread:创建线程,访问类的方法。

destroy:释放资源

2、编写方法获取UUID

public class UUIDUtils {
	public static String get(){
		return UUID.randomUUID().toString();
	}
}
复制代码

3、通过javah获得头文件com_example_ndkfile_NDKTest.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndkfile_NDKTest */

#ifndef _Included_com_example_ndkfile_NDKTest
#define _Included_com_example_ndkfile_NDKTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_ndkfile_NDKTest
 * Method:    getStrFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_ndkfile_NDKTest_getStrFromJNI
  (JNIEnv *, jclass);

//初始化
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_init
  (JNIEnv *, jobject);

//销毁
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_destroy
  (JNIEnv *, jobject);
//创建线程
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_pthread
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

复制代码

4、创建.c文件

5、项目右键--->Android Tools------->add native support

6、配置Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := myndk
LOCAL_SRC_FILES := myndk.c
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)
复制代码

7、在myndk.c实现com_example_ndkfile_NDKTest.h函数。

我们知道每个线程都有独立的JNIEnv,那么如何获取JNIEnv? 首先JavaVM 代表的是Java虚拟机,所有的工作都是从JavaVM开始,可以通过JavaVM获取到每个线程关联的JNIEnv。 那么又如何如何获取JavaVM?

1)、在JNI_OnLoad函数中获取,每次运行jni系统就会首先自动调用JNI_OnLoad函数。

2)、(*env)->GetJavaVM(env,&javaVM);

这里我们通过JNI_OnLoad获取:

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
	LOGI("%s","JNI_OnLoad");
	javaVM = vm;
	return JNI_VERSION_1_4;
}
复制代码
myndk.c:
#include "com_example_ndkfile_NDKTest.h"
#include <jni.h>
#include <stdio.h>
#include <pthread.h>
#include <android/log.h>
#include <unistd.h>

#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"test",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"test",FORMAT,##__VA_ARGS__);

JavaVM *javaVM;
jobject uuidutils_class_global;
jmethodID uuidutils_get_mid;

//动态库加载时会执行
//兼容Android SDK 2.2之后,2.2没有这个函数
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
	LOGI("%s","JNI_OnLoad");
	javaVM = vm;
	return JNI_VERSION_1_4;
}

JNIEXPORT jstring JNICALL Java_com_example_ndkfile_NDKTest_getStrFromJNI
  (JNIEnv *env, jclass jcls){
	return (*env)->NewStringUTF(env,"hello formjni");
}


void* th_fun(void* arg){
	int i;
	for (i = 0; i < 5; i++) {
		JNIEnv* env;
		//关联参数
		//JavaVMAttachArgs args = {JNI_VERSION_1_4, "my_thread", NULL};
		//(*javaVM)->AttachCurrentThread(javaVM,&env,&args);
		(*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
		jobject uuid_jstr = (*env)->CallStaticObjectMethod(env,uuidutils_class_global,uuidutils_get_mid);
		const char* uuid_cstr = (*env)->GetStringUTFChars(env,uuid_jstr,NULL);
		LOGI("uuid:%s",uuid_cstr);
		//退出线程
		if(i == 4){
			goto end;
		}
		sleep(1);
	}
end:
	//取消关联
	(*javaVM)->DetachCurrentThread(javaVM);
	pthread_exit((void*)0);

}

//初始化
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_init
(JNIEnv *env, jobject jobj){
	//获取class必须要在主线程中
	jclass uuidutils_class_tmp = (*env)->FindClass(env,"com/example/ndkfile/UUIDUtils");
	//创建全局引用
	uuidutils_class_global = (*env)->NewGlobalRef(env,uuidutils_class_tmp);
	//获取jmethodId也可以在子线程中
	uuidutils_get_mid = (*env)->GetStaticMethodID(env,uuidutils_class_global,"get","()Ljava/lang/String;");
}

//创建线程
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_pthread
(JNIEnv *env, jobject jobj){
	//(*env)->GetJavaVM(env,&javaVM);
	//创建多线程
	pthread_t tid;
	pthread_create(&tid, NULL,th_fun,(void*)"NO1");
}


//销毁
JNIEXPORT void JNICALL Java_com_example_ndkfile_NDKTest_destroy
(JNIEnv *env, jobject jobj){
	//释放全局引用
	(*env)->DeleteGlobalRef(env,uuidutils_class_global);

}

复制代码

1)、在初始化的时候还通过了(*env)->NewGlobalRef创建全局引用,在销毁的时候需要通过(*env)->DeleteGlobalRef释放全局引用。

2)、通过以下语句创建多线程,而th_fun方法就是运行在子线程中。

pthread_t tid; pthread_create(&tid, NULL,th_fun,(void*)"NO1");

调用

MainActivity:

public class MainActivity extends Activity {

	private TextView mTextView;
	private NDKTest ndktest;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mTextView = (TextView)this.findViewById(R.id.mytext);
		mTextView.setText(NDKTest.getStrFromJNI());
		
		ndktest = new NDKTest();
		ndktest.init();
		
		mTextView.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				ndktest.pthread();
			}
		});
		
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		ndktest.destroy();
	}
}
复制代码

注意: 需要在onCreate中调用init,在onDestroy调用destroy

运行结果:

从结果中我们看到了,确实通过JNI多线程访问java类获取得到了UUID。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值