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


一、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是任何處理域和方法的函數的必須參數。
 
二、jni中引用的java對象的生命周期
Java對象做為引用被傳遞到本地方法中,所有這些Java對象的引用都有一個共同的父類型jobject(相當於java中的 Object類是所有類的父類一樣)。 這些對象引用都有其生命周期。在JNI中對Java對象的引用根據生命周期分為:全局引用,局部引用、弱全局引用
1、Local Reference 本地引用,
函數調用時傳入jobject或者jni函數創建的jobejct,都是本地引用.
其特點就是一旦JNI層函數返回,jobject就被垃圾回收掉,所以需要注意其生命周期。可以強制調用DeleteLocalRef進行立即回收。
 jstring pathStr = env->NewStringUTF(path)
 ....
 env->DeleteLocalRef(pathStr);
2、Global Reference 全局引用 ,這種對象如不主動釋放,它永遠都不會被垃圾回收
 創建: env->NewGlobalRef(obj);
 釋放: env->DeleteGlobalRef(obj)
 若要在某個 Native 代碼返回后,還希望能繼續使用 JVM 提供的參數, 或者是過程中調用 JNI 函數的返回值(比如 g_mid), 則將該對象設為 global reference,以后只能使用這個 global reference;若不是一個 jobject,則無需這么做。
3、Weak Global Reference 弱全局引用
一種特殊的 Global Reference ,在運行過程中可能被垃圾回收掉,所以使用時請務必注意其生命周期及隨時可能被垃圾回收掉,比如內存不足時。
 使用前可以利用JNIEnv的 IsSameObject 進行判定它是否被回收
 env->IsSameObject(obj1,obj2);
 
三、本地線程中調用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
作者:流星


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM