JNI開發技術歸納

0.JNI概述

      在Android開發中會遇到使用JNI的情況,JNI是Java Native Interface的縮寫,即Java本地接口,通過JNI技術可以實現兩點:

      1)Java程序能夠調用Native函數,Native一般指的是C/C++

      2)Native函數能夠調用Java層的方法

1.JNI註冊
      JNI的註冊分成兩種:1)靜態註冊;2)動態註冊
      Java層實現調用Native函數的代碼示例如下:
public class JniHelper
{
    static {
         System.loadLibrary("jni_helper");
    }
    private static native void nativeFunc();
}

      其中jni_helper是jni層庫libjni_helper.so去掉lib後的名字,是由程序運行時系統加載,帶有native關鍵字的方法,說明此方法是native代碼實現的,也就是說該方法在庫libjni_helper.so中實現

      所謂的註冊,也就是將java層中的nativeFunc()對應到libjni_helper.so庫中的實現上,將聲明和實現聯繫起來

      1)靜態註冊的流程一般是,先編寫java層代碼,將java代碼編譯成class文件,再使用java提供的javah工具生成一個JNI層頭文件,例如:javah -o jni_helper packgename.JniHelper,packgename.JniHelper是class文件,生成一個名爲jni_helper.h的頭文件,其中Java層聲明的nativefunc()方法在JNI頭文件中會聲明爲
      JNIEXPORT void JNICALL Java_pack_age_name_JniHelper_nativeFunc(JNIEnv*, jclass);
      之所以將packagename寫成pack_age_name,是因爲包名中的"."會轉換成"_",此處僅作示例
      最後,根據這個生成的JNI層頭文件,對應實現native方法就可以了,在Java層調用Native函數的時候,系統會到JNI庫libjni_helper.so中去找對應的方法,找到的話就會爲Java層方法和庫中的方法(即JNI頭文件中那一長串的方法聲明)建立起關聯,這就是靜態註冊的方式
      2)實際上android中大都用的是動態註冊方式,這種方式下會用到一個結構體JNINativeMethod,在JNI層代碼中會定義一個JNINativeMethod數組

       static JNINativeMethod gMethod[] = {
      		{
        		"nativeFunc", //Java層方法聲明
        		"()V", //函數簽名
        		(void *)nativeFFFunc //JNI層對應的函數指針
      		}
      };

      對應的註冊函數如下:

int registerNativeMethod(JNIEnv * env, const char * className, const JNINativeMethod * gMethod, int numMethods)
{
     jclass clazz;
     clazz = (*env)->FindClass(env, className);
     
     if((*env)->RegisterNatives(env, clazz, gMethods, numMethods)<0)
     {
        return -1;
     }
     return 0;
}

      調用JNIEnv的RegisterNatives函數,就能夠動態註冊這種Java層函數與Native函數的關聯

      最後還需要實現JNI_Onload函數,該函數在JNI庫libjni_helper.so加載時會去調用,如果實現了該函數,則調用它,沒有實現則略過,當然在靜態註冊的時候也可以實現該函數,完成一些初始化工作,JNI_Onload函數示例如下:

jint JNI_Onload(JavaVM* vm, void* reserved)
{
      JNIEnv* env = NULL;
      jint result = -1;
      
      if(vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK)
      {
              return result;
      }
      //動態註冊JNI函數,className指明Java中的類,形如"com/example/JniHelper",包的"."由"/"代替
      registerNativeMethod(JNIEnv* env, const char* className, const JNINativeMethod* gMethod, int numMethods);
     
      return JNI_VERSION_1_4; //這個必須有,否則會報錯
}
2.數據類型轉換

      Java數據類型分爲基本數據類型和引用數據類型兩種

      1)基本數據類型轉換如下:

Java Native 符號 位數
boolean jboolean 無符號 8位
byte jbyte 無符號 8位
char jchar 無符號 16位
short jshort 有符號 16位
int jint 有符號 32位
long jlong 有符號 64位
float jfloat 有符號 32位
double jdouble 有符號 64位











      2)引用數據類型轉換如下:

Java Native
java.lang.Class jclass
java.lang.String jstring
java.lang.Throwable jthrowable
其他的Object jobject
Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray

3.JNI類型簽名

      因爲Java有函數重載的概念,所以單靠函數名不能區分函數,需要有簽名,簽名由參數類型和返回值組成,簽名格式:

      (參數1類型標識參數2類型標識......參數n類型標識)返回值類型標識

      類型標識表:

Java類型 類型標識
boolean Z
byte B
char C
short S
int I
long J
float F
double D
String L/java/langaugeString;
int[] [I
Object[] [L/java/lang/object;
      數組類型前面會有一個"[",引用類型最後有一個";",簽名示例如下:

      String f() 的簽名是()Ljava/lang/String;

      int f(char c, int i)的簽名是(CI)I 

      簽名寫起來麻煩,看起來也彆扭,不過java有一個javap工具能幫我們生成簽名信息,用法如:javap -s -p classfile, classfile是編譯後的class文件,-s輸出內部數據類型的簽名信息,-p打印所有函數和成員的簽名信息

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