之前的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/* Header for class com_example_ndkfile_NDKTest */#ifndef _Included_com_example_ndkfile_NDKTest#define _Included_com_example_ndkfile_NDKTest#ifdef __cplusplusextern "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 := myndkLOCAL_SRC_FILES := myndk.cLOCAL_LDLIBS := -lloginclude $(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#include #include #include #include #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。