Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android JNI 之 JNIEnv 解析

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 在本線程的運行環境 ;


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

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

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


JNIEnv 作用 :

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

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


2. JNIEnv 的創建和釋放


JNIEnv 創建 和 釋放 : 從 JavaVM 獲得 : 下面是 JavaVM 結構體的代碼,

-- 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 函數;


\



<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD48aDI+NC4gt9bO9iBKTklFbnYgz+C52LT6wus8L2gyPjxwPjxiciAvPjwvcD48cD48L3A+PHA+PHN0cm9uZz5KTklFbnYgtqjS5bXEz+C52LT6wus8L3N0cm9uZz4gOiA8L3A+PHA+PC9wPjxwcmUgY2xhc3M9"brush:java;">/* 聲明結構體, 以便在下面能夠使用 */ 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*/
};


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved