5分鐘理解Android中的JNI原理!!!


        JNI(Java Native Interface)是Java本地接口,用來與其他的語言進行通信。通常情況下,當需要節省程序的運行時間時,會選擇用C語言進行開發,用Java調用C語言實現一些功能。或者代碼的核心算法、密鑰等,也會保存在C語言的代碼中,以提高代碼的安全性。

一.Native方法的註冊

1.靜態註冊

1)靜態註冊的原理

        根據方法名,將java方法和JNI函數建立關聯。

2)靜態註冊的方法

        i)在項目中創建Test.java

package com.example;
public class Test{
   static{
      System.loadLibrary(“test_jni”);
   }
   public native void test();
}

        ii)打開命令行,進入項目的src/main/java目錄下,執行

javac com/example/Test.java
javah com.example.Test

        會在當前目錄下,生成com_example_Test.h文件。在com_example_Test.h文件中,生成了一個名爲Java_com_example_test的函數。
        iii)當在Java中調用test方法時,JNI會尋找Java_com_example_test函數,找到後爲Java方法和JNI的函數建立聯繫,完成靜態註冊。

3)靜態註冊的缺點

        i)JNI層的函數名過長。
        ii)聲明Native方法的類需要用javah生成頭文件。
        iii)初次調用Native方法時需要建立聯繫,影響效率。

2.動態註冊

        JNI中有一種結構JNINativeMethod,用來記錄Java的Native方法和JNI方法的關聯關係,他在jni.h中定義:

Typedef struct{
   const char* name;//java方法的名字
   const char* signature;//java方法的簽名信息
   void* fnPtr;//JNI中對應的方法指針
} JNINativeMethod;

1)動態註冊的方法

        i)創建JNINativeMethod數組:

static const JNINativeMethod methods[] = {
   {“test1”,()v”,(void *)com_example_Test_test1},
   {“test2”,()v”,(void *)com_example_Test_test2},
   {“test3”,()v”,(void *)com_example_Test_test3},
   {“test4”,()v”,(void *)com_example_Test_test4}
};

        ii)調用AndroidRuntime::registerNativeMethods函數完成註冊。

2)解析AndroidRuntime::registerNativeMethods函數

        AndroidRuntime.cpp中registerNativeMethods函數的執行過程:
        調用jniRegisterNativeMethods函數。

        JNIHelp.cpp中jniRegisterNativeMethods函數的執行過程:
        調用JNIEnv對象的RegisterNative函數完成JNI方法的註冊。

二.數據類型的轉換

1.基本數據類型的轉換

Java Native Signature
byte jbyte B
char jchar C
double jdouble D
float jfloat F
int jint I
short jshort S
long jlong J
boolean jboolean Z
void void V

2.引用數據類型的轉換

Java Native Signature
Object jobject L+classname+;
Class jclass Ljava/long/Class;
String jstring Ljava/lang/String;
Throwable jthrowable Ljava/lang/Throwable;
Object[] jobjectArray [L+classname+;
byte[] jbyteArray [B
char[] jcharArray [C
double[] jdoubleArray [D
float[] jfloatArray [F
int[] jintArray [I
short[] jshortArray [S
long[] jlongArray [J
boolean[] jbooleanArray [Z

3.引用數據類型的繼承關係

在這裏插入圖片描述

三.方法簽名

        JNI通過引入方法簽名來解決Java中方法重載導致的僅通過方法名無法找到方法的問題。

1.JNI方法簽名的格式

        格式:(參數的簽名格式…)返回值的簽名格式

        java中提供javap命令自動生成方法簽名:

javac C:/Test.java
javap –s –p C:/Test.class

四.解析JNIEnv

        JNIEnv是Native中Java環境的代表,用來調用Java的方法、操作Java的變量和對象。

        jni.h中JNIEnv的定義:

#if defined (_cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

        JavaVM是虛擬機層在JNI層的代表,一個虛擬機進程只有一個JavaVM。通過JavaVM的AttachCurrentThread函數可以獲取一個線程的JNIEnv,通過DetachCurrentThread函數釋放資源。

        jni.h中_JNIEnv結構體的定義和部分函數:

struct _JNIEnv{
  const struct JNINativeInterface* functions;
#if defined (_cplusplus)
…
jclass FindClass (const char* name)
{ return functions->FindClass (this, name); }
…
  jmethodID GetMethodID (jclass clazz, const char* name, const char* sig)
  { return functions->GetMethodID (this, clazz, name, sig); }
  …
  jfieldID GetFieldID (jclass clazz, const char* name, const char* sig)
  { return functions->GetFieldID (this, clazz, name, sig); }
}

        _JNIEnv結構體內部包含了JNINativeInterface結構體。其定義的函數通過調用JNINativeInterface的函數來實現。

        jni.h中JNINativeInterface結構體的定義和部分函數:

sruct JNINativeInterface{
…
jclass (*FindClass) (JNIEnv*, const char* );
…
jmethodID (*GetMethodID) (JNIEnv*, jclass, const char*, const char*);
…
jfieldID (*GetFieldID) (JNIEnv*, jclass, const char*, const char*);}

        在JNINativeInterface結構體中定義了很多函數指針,通過這些函數指針,能夠定位到虛擬機中的JNI函數表,實現JNI層在虛擬機中的函數調用。
        jfieldID和jmethodID用來代替Java類中的成員變量和方法。

五.JNI的引用類型

1.本地引用

        JNIEnv提供的函數所返回的引用基本上都是本地引用。
特點:
        1)Native函數返回時,本地引用自動釋放。
        2)只在創建它的線程有效,不能跨線程使用。
        3)本地引用是JVM負責的引用類型,受JVM管理。
        可以通過DeleteLocalRef函數手動刪除本地引用。

2.全局引用

特點:
        1)在Native函數返回時不會自動釋放,需要手動釋放,不會被GC回收。
        2)全局引用可以跨線程使用。
        3)全局引用不受JVM管理。
        可以通過NewGlobalRef函數來創建全局引用,通過DeleteGlobalRef函數釋放全局引用。

3.弱全局引用

        弱全局引用是一種特殊的全局引用,和全局引用類似,弱全局引用可以被GC回收,回收之後會指向NULL。
        可以通過NewWeakGlobalRef函數來創建弱全局引用,通過DeleteWeakGlobalRef函數釋放弱全局引用。在使用前需要使用JNIEvn的IsSameObject函數判斷對象是否被回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章