之前的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。