【Android 系統開發】Android JNI 之 JNIEnv 解析

.android


jni.h文件 : 瞭解 JNI 需要配合 jni.h 文件, jni.h 是 Google NDK 中的一個文件, 位置是 $/android-ndk-r9d/platforms/android-19/arch-arm/usr/include/jni.h ;數組


1. JNIEnv 做用


JNIEnv 概念 : 是一個線程相關的結構體, 該結構體表明瞭 Java 在本線程的執行環境 ; app


JNIEnv 與 JavaVM : 注意區分這兩個概念; 函數

-- JavaVM : JavaVM 是 Java虛擬機在 JNI 層的表明, JNI 全局僅僅有一個;post

-- JNIEnv : JavaVM 在線程中的表明, 每個線程都有一個, JNI 中可能有很是多個 JNIEnv;this


JNIEnv 做用spa

-- 調用 Java 函數 : JNIEnv 表明 Java 執行環境, 可以使用 JNIEnv 調用 Java 中的代碼;線程

-- 操做 Java 對象 : Java 對象傳入 JNI 層就是 Jobject 對象, 需要使用 JNIEnv 來操做這個 Java 對象;指針


2. JNIEnv 的建立和釋放


JNIEnv 建立 和 釋放 : 從 JavaVM 得到 : 如下是 JavaVM 結構體的代碼, code

-- C語言 中來源 : JNIInvokeInterface 是 C 語言環境中的 JavaVM 結構體, 調用 (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*) 方法, 可以獲取 JNIEnv結構體;

-- C++ 中來源 : _JavaVM 是 C++ 中的 JavaVM 結構體, 調用 jint AttachCurrentThread(JNIEnv** p_env, void* thr_args) 方法, 可以獲取 JNIEnv 結構體;

-- C語言 中釋放 : 調用 JavaVM結構體 (JNIInvokeInterface) 中的 (*DetachCurrentThread)(JavaVM*)方法, 可以釋放本線程中的 JNIEnv;

-- C++ 中釋放 : 調用 JavaVM 結構體 (_JavaVM) 中的 jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); } 方法, 就能夠釋放 本線程中的 JNIEnv ;

/*
 * JNI invocation interface.
 */
struct JNIInvokeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;

    jint        (*DestroyJavaVM)(JavaVM*);
		/* 建立 JNIEnv , 每個線程建立一個 */
    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
		/* 釋放本線程的 JNIEnv */
    jint        (*DetachCurrentThread)(JavaVM*);
    jint        (*GetEnv)(JavaVM*, void**, jint);
    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};

/*
 * C++ version.
 */
struct _JavaVM {
    const struct JNIInvokeInterface* functions;

#if defined(__cplusplus)
    jint DestroyJavaVM()
    { return functions->DestroyJavaVM(this); }
	/* 建立 JNIEnv , 每個線程建立一個 , 調用的C語言結構提中的方法, C 與 C++ 方法一樣 */
    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThread(this, p_env, thr_args); }
	/* 釋放本線程的 JNIEnv , 調用的C語言結構提中的方法, C 與 C++ 方法一樣 */
    jint DetachCurrentThread()
    { return functions->DetachCurrentThread(this); }
    jint GetEnv(void** env, jint version)
    { return functions->GetEnv(this, env, version); }
    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};


3. JNIEnv 體系結構 


線程相關 : JNIEnv 是線程相關的, 即 在 每個線程中 都有一個 JNIEnv 指針, 每個JNIEnv 都是線程專有的, 其餘線程不能使用本線程中的 JNIEnv, 線程 A 不能調用 線程 B 的 JNIEnv;


JNIEnv 不能跨線程

-- 當前線程有效 : JNIEnv 僅僅在當前線程有效, JNIEnv 不能在 線程之間進行傳遞, 在同一個線程中, 屢次調用 JNI層方法, 傳入的 JNIEnv 是一樣的;

-- 本地方法匹配多JNIEnv : 在 Java 層定義的本地方法, 可以在不一樣的線程調用, 所以 可以接受不一樣的 JNIEnv;


JNIEnv 結構 : 由上面的代碼可以得出, JNIEnv 是一個指針,  指向一個線程相關的結構, 線程相關結構指向 JNI 函數指針 數組, 這個數組中存放了大量的 JNI 函數指針, 這些指針指向了詳細的 JNI 函數; 





4. 分析 JNIEnv 相關代碼


JNIEnv 定義的相關代碼 : 

/* 聲明結構體, 以便在如下可以使用 */
struct _JNIEnv;
struct _JavaVM;
/* 聲明 C 語言環境中的 JNIEnv 爲 C_JNIEnv 指針, 指向 JNINativeInterface 結構體 */
typedef const struct JNINativeInterface* C_JNIEnv;

#if defined(__cplusplus)
/* C++環境下, JNIEnv 是結構體 */
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
/* C語言環境下, JNIEnv是指針 */
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

-- JNINativeInterface 結構體 : 該結構體中定義了大量的函數指針, 這些函數指針 指向 與 Java 相關的變量有關的函數, 假設是 C 語言環境中, JNIEnv 就是指向 該結構體的指針;

-- _JNIEnv 結構體 : C++ 環境中的 JNIEnv 就是該結構體, 該結構體中封裝了 一個 JNINativeInterface 結構體指針, 即 C++ 中的 JNIEnv 要比 C 語言中的要多, 並且 全然兼容 C 語言中的 JNIEnv;

-- _JavaVM 結構體 : 該結構體 是 Java 虛擬機 在 JNI 中的表明, 整個 JNI 層 僅僅存在一個 該 虛擬機映射;


JNINativeInterface 源代碼(刪減過) : 省略後的, 當中定義了 與 Java 有關的相關方法, 都是 指向相應函數的函數指針;

-- 解析 JNIEnv* : C語言環境中的 typedef const struct JNINativeInterface* JNIEnv , JNIEnv* env 等價於 JNINativeInterface** env1 (指向結構體地址的指針), 要想 依據 二級指針 env1 獲取 JNINativeInterface 結構體中定義的函數指針, 首先獲取 指向結構體的一級指針, 獲取方法是 (*env1), 所以調用當中的函數指針指向的方法要這樣 : (*env1)->FindClass(JNIEnv*, const char*);

/*
 * Table of interface function pointers.
 */
struct JNINativeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;
    void*       reserved3;

    jint        (*GetVersion)(JNIEnv *);

    ... ...

    jobject     (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
    void*       (*GetDirectBufferAddress)(JNIEnv*, jobject);
    jlong       (*GetDirectBufferCapacity)(JNIEnv*, jobject);

    /* added in JNI 1.6 */
    jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
};


_JNIEnv 源代碼(刪減過) : 該源代碼中有一個 JNINativeInterface 結構體指針, 說明 C++ 環境的 JNIEnv 是在 C 語言環境的 JNIEnv 下擴展的;

-- 解析 JNIEnv* : 定義是這種 typedef _JNIEnv JNIEnv, JNIEnv* env 等價於 _JNIEnv* env1, 所以調用 _JNIEnv 中定義的函數指針指向的函數的時候, 僅僅需要 使用 env1->FindClass(JNIEnv*, const char*) 就能夠;

/*
 * C++ object wrapper.
 *
 * This is usually overlaid on a C struct whose first element is a
 * JNINativeInterface*.  We rely somewhat on compiler behavior.
 */
struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)

    jint GetVersion()
    { return functions->GetVersion(this); }

    ... ... 

    jlong GetDirectBufferCapacity(jobject buf)
    { return functions->GetDirectBufferCapacity(this, buf); }

    /* added in JNI 1.6 */
    jobjectRefType GetObjectRefType(jobject obj)
    { return functions->GetObjectRefType(this, obj); }
#endif /*__cplusplus*/
};
相關文章
相關標籤/搜索