1. 程式人生 > >Android中關於JNI 的學習(一)對於JNIEnv的一些認識

Android中關於JNI 的學習(一)對於JNIEnv的一些認識

else size 初步 jint 使用 包括 pri jnienv 就會

一個簡單的樣例讓我們初步地了解JNI的作用,可是關於JNI中的一些概念還是須要了解清楚,才可以更好的去利用它來實現我們想要做的事情。

那麽C++和Java之間的是怎樣通過JNI來進行互相調用的呢?

我們知道。在Android中,當Java文件被編譯成dex文件之後,會由類載入器載入到Dalvik VM(DVM)中,由DVM來進行解釋,翻譯成機器語言之後,才幹由機器來執行。

而對於C/C++來說,其源碼經由Android提供的NDK工具包,能夠編譯成可運行動態庫(即.so文件)。之後。Java和C++之間就能夠進行通訊了。

那麽,在這裏,能夠想像,Java的Dex字節碼和C/C++的so庫肯定是同一時候執行在一個DVM之中。它們是共同使用一個進程空間的,否則,它們怎麽彼此溝通呢?

所以在這裏,一個關鍵的中間區域就是Dalvik VM。而對於C/C++,當它們也被載入進DVM之後,由C/C++實現的函數方法等都會被載入在DVM中的函數表中。

假設想要在C/C++中調用函數,它們必需要有個東西可以讓其訪問到這個虛擬機中的函數表。

而這個東西就是JNIEnv *。

當我們利用javah生成的C/C++的頭文件的時候。例如以下:

JNIEXPORT jstring JNICALL Java_com_lms_jni_HwDemo_printHello
  (JNIEnv *e, jobject j)
{
        return (**e).NewStringUTF(e,"Hello from T" );
}

我們能夠看到這種方法有兩個參數。當中第一個就是JNIEnv *。而我們在Java端定義這種方法的時候。是沒有參數的,例如以下:

public native String printHello();

那麽這個JNIEnv是幹什麽用的? 事實上從這個參數的名稱就能夠看到,就是指JNI的執行環境,我認為它就是對Java虛擬環境的一個引用。在Android中。就是指Dalvik VM。 參考jni.h文件裏關於JNIEnv的定義,例如以下(對於C和C++。它的定義有點不一樣):
struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv; //C++中JNIEnv的類型
typedef _JavaVM JavaVM; 
#else
typedef const struct JNINativeInterface* JNIEnv; //C中JNIEnv的類型
typedef const struct JNIInvokeInterface* JavaVM;
#endif

在C中,我們能夠看到JNIEnv的類型就是JNINativeInterface* 。是一個指針類型,那麽在C++中呢,_JNIEnv是什麽樣的呢?
struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

而對於C++來說。 _JNIEnv是一個結構體。裏面包括了JNINativeInterface*的結構。 所以從這裏也能夠看到。對於C和C++來說,它們引用JNIEnv中的方法是有一點不一樣的。總的來說,JNIEnv。無論是C。還是C++,事實上關鍵都是JNINativeInterface的這個結構。

我們能夠簡單看一下JNINativeInterface結構的定義,例如以下:
struct JNINativeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;
    void*       reserved3;

    jint        (*GetVersion)(JNIEnv *);

    jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
                        jsize);
    jclass      (*FindClass)(JNIEnv*, const char*);

    jmethodID   (*FromReflectedMethod)(JNIEnv*, jobject);
    jfieldID    (*FromReflectedField)(JNIEnv*, jobject);
    /* spec doesn‘t show jboolean parameter */
    jobject     (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);

能夠看到在它當中定義了非常多的函數指針,而通過這些定義。JNI層事實上就獲得了對DVM的引用,通過定義的這些函數指針。能夠定位到虛擬機中的 JNI 函數表,從而實現JNI層在DVM中的函數調用。
所以。可以這樣理解,事實上JNIEnv,就是對DVM執行環境中C/C++函數的一個引用。而也正由於此,當C/C++想要在DVM中調用函數的時候。由於其是在DVM的環境中,所以它們必須通過JNIEnv* 這個參數來獲得這些方法,之後才可以使用。


那麽這個JNIEnv是什麽時候產生的呢?
當Android中第一個Java線程要調用本地的C/C++代碼的時候。DVM就會為該線程產生一個JNIEnv*的指針。

而每個線程在和C/C++互相調用的時候。其相應的JNIEnv 也是相互獨立。


嗯。結束。


Android中關於JNI 的學習(一)對於JNIEnv的一些認識