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
做者:流星