JNIEnv說明

JavaVM接口

第一種方式,在加載動態連接庫的時候,JVM會調用JNI_OnLoad(JavaVM* jvm, void* reserved)(若是定義了該函數)。第一個參數會傳入JavaVM指針。c++

第二種方式,在native code中調用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)能夠獲得JavaVM指針。jvm

兩種狀況下,均可以用全局變量,好比JavaVM* g_jvm來保存得到的指針以便在任意上下文中使用。函數

Android系統是利用第二種方式Invocation interface來建立JVM的。this

JNIEnv接口

須要強調的是JNIEnv是跟線程相關的。spa

在native method中,JNIEnv做爲第一個參數傳入。那麼在JNIEnv不做爲參數傳入的時候,該如何得到它?線程

JNI提供了兩個函數:設計

(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)指針

 (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)code

 兩個函數都利用JavaVM接口得到JNIEnv接口,上面已經講到如何得到JavaVM接口。orm

 JNI規範也說明,能夠將得到JNIEnv封裝成一個函數。

1
2
3
4
5
6
JNIEnv* JNU_GetEnv()
{
             JNIEnv* env;
             (*g_jvm)->GetEnv(g_jvm, (         void         **)&env, JNI_VERSION_1_2);
             return          env;
}

Java經過JNI機制調用c/c++寫的native程序。c/c++開發的native程序須要遵循必定的JNI規範,下面的例子就是一個JNI函數聲明:

JNIEXPORT jint JNICALL Java_jnitest_MyTest_test
  (JNIEnv 
* env, jobject obj, jint arg0);

JVM負責從Java Stack轉入C/C++ Native Stack。當Java進入JNI調用,除了函數自己的參數(arg0),會多出兩個參數:JNIEnv指針和jobject指針。
JNIEnv指針是JVM建立的,用於Native的c/c++方法操縱Java執行棧中的數據,好比Java Class, Java Method等。
首先,JNI對於JNIEnv的使用, 提供了兩種語法: c語法以及c++語法,以下:
c語法:

jsize len = (*env)->GetArrayLength(env,array);

c++語法:

jsize len =env->GetArrayLength(array);

(注:因爲C語言並不支持對象的概念,因此C語法中須要把env做爲第一個參數傳入,相似於C++的隱式參數this指針).

對於JNIEnv *env來講,在C中調用:

(*env)->NewStringUTF(env, "Hello from JNI!");

而在C++中若是按照上述調用則會發生'base operand of '->' has non-pointer type '_JNIEnv''錯誤,須要以下調用:

env->NewStringUTF("Hello from JNI!");

緣由:參見jni.h中對於JNIEnv的定義:

#if defined(__cplusplus)

typedef _JNIEnv JNIEnv;

#else

typedef const struct JNINativeInterface* JNIEnv;

#endif
另外: JNIEnv有幾個設計的原則:
第1、JNIEnv指針被設計成了Thread Local Storage(TLS)變量,也就是說每個Thread, JNIEnv變量都有獨立的Copy。你不能把Thead#1使用的JNIEnv傳給Thread#2使用。

第 2、JNIEnv中定義了一組函數指針,c/c++ Native程序是經過這些函數指針操縱Java數據。這樣設計的好處是:你的c/c++ 程序不須要依賴任何函數庫,或者DLL。因爲JVM可能由不一樣的廠商實現,不一樣廠商有本身不一樣的JNI實現,若是要求這些廠商暴露約定好的一些頭文件和 庫,這不是靈活的設計。
並且使用函數指針表的另一個好處是: JVM能夠根據啓動參數動態替換JNI實現。

相關文章
相關標籤/搜索