JNI 靜態註冊和動態註冊

JNI 靜態註冊和動態註冊

靜態註冊
  • 註冊函數說明
    java 層聲明 native 關鍵字修飾的函數,再使用 javah 編譯得到 c/c++ 的頭文件(.h),其包含 java_完整包名_類名_方法名 命名規則的橋接層函數。
    以下展示生成的頭文件代碼:
    #include <jni.h>
    
    #ifndef _JNI_DEMO_GREET_H // 避免頭文件重複引用
    #define _JNI_DEMO_GREET_H
    
    #ifdef __cplusplus // c++ 支持函數重載,會在編譯階段把入參類型拼接在函數名後,這裏註明使用 c 的編譯方式.
    extern "C" {
    #endif
    
    JNIEXPORT jstring JNICALL Java_com_jnidemo_Greet_sayHello(JNIEnv *env, jclass clazz);
    JNIEXPORT void JNICALL Java_com_jnidemo_Greet_sayBye(JNIEnv *env, jclass clazz);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif // _JNI_DEMO_GREET_H
    
  • 靜態註冊的痛點
    • java 層包名/類名/函數名,任一處有改動,頭文件及其實現函數的函數名稱都會失效,需手動修改維護,查找繁瑣且易出錯。
    • 所有包含 native 函數的 java 類都需要編寫 JNI頭文件,且函數名過長。
    • 初次運行時需要建立 java與native 的jni函數映射關係, 影響運行效率。
動態註冊
  • 註冊流程
    Java-System.loadLibrary(libname) —>
    C-JNI_OnLoad —>
    Env->FindClass(classname) —>
    Env->RegisterNatives(JNINativeMethod[] - 函數映射表).

  • 註冊函數說明
    以下展示 c/c++ 層橋接層代碼:

      #include <jni.h>
    
      /**
       * JNINativeMethod 結構體描述
       * const char* name;      native函數名,之後java層函數名修改時同步修改該字段,包名類名修改時同步修改classname.
       * const char* signature; JNI 函數簽名,需遵循橋接層簽名規則。
       * void*       fnPtr;     c層實現函數的函數指針。
       */
      static JNINativeMethod gMethods1[] = {
              {"sayHello",         "()Ljava/lang/String;",     (void *) JniSayHello},
              {"sayBye",           "()V",                      (void *) JniSayBye}
      };
      
      static int registerNativesMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods, int numMethods) {
          jclass clazz = env->FindClass(className);
          if (clazz == NULL)
              return JNI_FALSE;
      
          if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
              return JNI_FALSE;
          return JNI_TRUE;
      }
      
      static int registerNatives(JNIEnv *env) {
          // 這裏可以註冊多個包含native函數的Java類
          int ret1 = registerNativesMethods(env, classname1, gMethods1, sizeof(gMethods1) / sizeof(gMethods1[0]));
          int ret2 = registerNativesMethods(env, classname2, gMethods2, sizeof(gMethods2) / sizeof(gMethods2[0]));
          return (ret1 && ret2) ? JNI_TRUE : JNI_FALSE;
      }
      
      JNIEXPORT jint JNICALL
      JNI_OnLoad(JavaVM *vm, void *reserved) {
          JNIEnv *env = NULL;
          if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
              return -1;
      
          assert(env != NULL);
          if (!registerNatives(env))
              return -1;
          return JNI_VERSION_1_6;
      }
    

參考文章:
JNI原理分析

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