一、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<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<jni.h>
#include<android/log.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
作者:流星