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