深刻了解android平臺的jni---本地多線程調用java代碼

1、jni調用java對象
    JNI提供的功能之一是在本地代碼中使用Java對象。包括:建立一個java類對象和經過函數傳遞一個java對象。建立一個java類對象,首先須要獲得獲得使用FindClass/GetObjectClass函數獲得該類,而後使用GetMethodID方法獲得該類的方法id,而後調用該函數。 Java 和 Native 代碼之間函數調用時,若是是簡單類型,也就是內置類型,好比 int, char 等是值傳遞(pass by value),而其它 Java 對象都是引用傳遞(pass by reference),這些對象引用由 JVM 傳給 Native 代碼。
在本地方法中調用Java對象的方法的步驟:
1)獲取你須要訪問的Java對象的類
FindClass經過傳java中完整的類名來查找java的class
GetObjectClass經過傳入jni中的一個java的引用來獲取該引用的類型。
他們之間的區別是,前者要求你必須知道完整的類名,後者要求在Jni有一個類的引用。
2)獲取MethodID,調用方法
GetMethodID 獲得一個實例的方法的ID 
GetStaticMethodID 獲得一個靜態方法的ID 
3)獲取對象的屬性
GetFieldID 獲得一個實例的域的ID 
GetStaticFieldID 獲得一個靜態的域的ID
JNI經過ID識別域和方法,一個域或方法的ID是任何處理域和方法的函數的必須參數。
 
2、jni中引用的java對象的生命週期
Java對象作爲引用被傳遞到本地方法中,全部這些Java對象的引用都有一個共同的父類型jobject(至關於java中的 Object類是全部類的父類同樣)。 這些對象引用都有其生命週期。在JNI中對Java對象的引用根據生命週期分爲:全局引用,局部引用、弱全局引用
一、Local Reference 本地引用,
函數調用時傳入jobject或者jni函數建立的jobejct,都是本地引用.
其特色就是一旦JNI層函數返回,jobject就被垃圾回收掉,因此須要注意其生命週期。能夠強制調用DeleteLocalRef進行當即回收。
 jstring pathStr = env->NewStringUTF(path)
 ....
 env->DeleteLocalRef(pathStr);
二、Global Reference 全局引用 ,這種對象如不主動釋放,它永遠都不會被垃圾回收
 建立: env->NewGlobalRef(obj);
 釋放: env->DeleteGlobalRef(obj)
 若要在某個 Native 代碼返回後,還但願能繼續使用 JVM 提供的參數, 或者是過程當中調用 JNI 函數的返回值(好比 g_mid), 則將該對象設爲 global reference,之後只能使用這個 global reference;若不是一個 jobject,則無需這麼作。
三、Weak Global Reference 弱全局引用
一種特殊的 Global Reference ,在運行過程當中可能被垃圾回收掉,因此使用時請務必注意其生命週期及隨時可能被垃圾回收掉,好比內存不足時。
 使用前能夠利用JNIEnv的 IsSameObject 進行斷定它是否被回收
 env->IsSameObject(obj1,obj2);
 
3、本地線程中調用java對象
問題1:
JNIEnv是一個線程相關的變量
JNIEnv 對於每一個 thread 而言是惟一的 
JNIEnv *env指針不能夠爲多個線程共用
解決辦法:
可是java虛擬機的JavaVM指針是整個jvm公用的,咱們能夠經過JavaVM來獲得當前線程的JNIEnv指針.
可使用javaAttachThread保證取得當前線程的Jni環境變量
static JavaVM *gs_jvm=NULL;
gs_jvm->AttachCurrentThread((void **)&env, NULL);//附加當前線程到一個Java虛擬機
jclass cls = env->GetObjectClass(gs_object);
jfieldID fieldPtr = env->GetFieldID(cls,"value","I");
問題2:
不能直接保存一個線程中的jobject指針到全局變量中,而後在另一個線程中使用它。
解決辦法:
用env->NewGlobalRef建立一個全局變量,將傳入的obj(局部變量)保存到全局變量中,其餘線程可使用這個全局變量來操縱這個java對象
注意:若不是一個 jobject,則不須要這麼作。如:
jclass 是由 jobject public 繼承而來的子類,因此它固然是一個 jobject,須要建立一個 global reference 以便往後使用。
而 jmethodID/jfieldID 與 jobject 沒有繼承關係,它不是一個 jobject,只是個整數,因此不存在被釋放與否的問題,可保存後直接使用。
static jobject gs_object=NULL;
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj)
{
    env->GetJavaVM(&gs_jvm); //保存到全局變量中JVM 
    //直接賦值obj到全局變量是不行的,應該調用如下函數: 
    gs_object=env->NewGlobalRef(obj);
}
 
jni部分代碼以下:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<jni.h>
#include<android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
 
//全局變量
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
void *thread_fun(void* arg)
 {
     JNIEnv *env;
     jclass cls;
     jmethodID mid;
 
     //Attach主線程
     if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
     {
         LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
         return NULL;
     }
     //找到對應的類
     cls = (*env)->GetObjectClass(env,g_obj);
     if(cls == NULL)
     {
         LOGE("FindClass() Error.....");
         goto error;
     }
     //再得到類中的方法
     mid = (*env)->GetMethodID(env, cls, "fromJNI", "(I)V");
     if (mid == NULL)
     {
         LOGE("GetMethodID() Error.....");
         goto error; 
     }
     //最後調用java中的靜態方法
         (*env)->CallVoidMethod(env, cls, mid ,(int)arg);
   
 
 error:   
     //Detach主線程
     if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
     {
         LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
     }
    
 
     pthread_exit(0);
 }
 
 //由java調用以建立子線程
 JNIEXPORT void Java_com_test_JniThreadTestActivity_mainThread( JNIEnv* env, jobject obj, jint threadNum)
 {
     int i;
     pthread_t* pt;
     pt = (pthread_t*) malloc(threadNum * sizeof(pthread_t));
     for (i = 0; i < threadNum; i++){
         //建立子線程
        pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
}
 
for (i = 0; i < threadNum; i++){
pthread_join (pt[i], NULL);
}
LOGE("main thread exit.....");
}
 
 
//由java調用來創建JNI環境
JNIEXPORT void Java_com_test_JniThreadTestActivity_setJNIEnv( JNIEnv* env, jobject obj)
 {
     //保存全局JVM以便在子線程中使用
     (*env)->GetJavaVM(env,&g_jvm);
     //不能直接賦值(g_obj = obj)
     g_obj = (*env)->NewGlobalRef(env,obj);
 }
 
 
 //當動態庫被加載時這個函數被系統調用
 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     JNIEnv* env = NULL;
     jint result = -1;   
 
     //獲取JNI版本
     if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
     {
         LOGE("GetEnv failed!");
             return result;
     }
 
     return JNI_VERSION_1_4;
 }
須要所有源碼的,能夠打開這個連接下載
http://download.csdn.net/detail/mfcai_blog/5772377
 
本文歡迎轉載,轉載請註明出處與做者
出處:http://blog.sina.com.cn/staratsky
做者:流星
相關文章
相關標籤/搜索