1. 程式人生 > >呼叫jni的兩種方法javah和RegisterNatives

呼叫jni的兩種方法javah和RegisterNatives

前言:

呼叫jni的有兩種方法:
一種是通過javah 自動生成jni檔案
一種是載入類庫的時候,呼叫JNIEnv 指標的RegisterNatives方法註冊native方法

RegisterNatives方式有3種好處
1、jni種函式命名自由,不必像javah自動生成的函式宣告那樣,必須特定的命名方式;
2、效率高。傳統方式下,Java類call本地函式時,通常是依靠VM去動態尋找.so中的本地函式(因此它們才需要特定規則的命名格式),而使用RegisterNatives將本地函式向VM進行登記,可以讓其更有效率的找到函式;

3、執行時動態調整本地函式與Java函式值之間的對映關係,只需要多次call RegisterNatives()方法,並傳入不同的對映表引數即可。

一、javah生成jni

  1. java類中宣告 native 方法
 public native String stringFromJNI();
  1. 通過javah方法生成jni檔案。(cmd命令進入包根目錄,javah 包名.類名)

生成方法會帶JNIEXPORT void JNICALL 方法名很長,包名和native方法結合在一起。如:

//返回型別
JNIEXPORT jstring
JNICALL
//生成的方法名字
Java_libyuv_aimissu_com_libyuvtestdemo_MainActivity_stringFromJNI(
        JNIEnv *env
, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }

二、RegisterNatives 註冊native方法

1. java類中宣告 native 方法
public native String stringFromJNI();
2. 定義JNINativeMethod集合,對應java類中native方法
/**
 * native方法集合
 */
static JNINativeMethod mynative_methods[] = {
        //java類中宣告的native方法名字   (引數,引數。。。)返回值型別             jni中對應的方法
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI}, {"getTestMethod", "(Ljava/lang/String;IJD)I", (void *) getTestMethod} };

關鍵是JNINativeMethod集合不要寫錯,引數和返回值都要和java類中宣告的對應上
一個JNINativeMethod對應一個native方法

JNINativeMethod 的結構體
typedef struct {
    //java類中宣告的native方法名字
    const char* name;
    //方法簽名(引數型別,引數型別。。。)返回值型別 
    const char* signature;
    jni中對應的方法
    void*       fnPtr;
} JNINativeMethod;

屬性signature簽名
()表示沒有引數,
(I)表示一個整形引數,
(I)I 表示一個整形引數返回值是整形,
(Ljava/lang/String;D[I)J 表示有三個引數一個字串,一個double型別,一個int []陣列,J表示long返回值

對應於Java的資料型別,對應native 型別,對應簽名

java型別 native 型別 型別簽名
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong J
float jfloat F
double jdouble D
L 全限定類名
陣列 [ 元素組型別簽名

如和對應簽名
public native int getTestMethod2(String str,int length,long b,double[] c);

上面這個方法對應的簽名是: (Ljava/lang/String;IJD)I

3. RegisterNatives註冊JNINativeMethod集合

3.1 jni中覆寫JNI_OnLoad(JavaVM *vm, void *reserved) 方法,java類中載入類庫System.loadLibrary(“”)的時候回執行這個方法

3.2 RegisterNatives註冊JNINativeMethod集合
在JNI_OnLoad中獲取JNIEnv 指標,通過JNIEnv 呼叫RegisterNatives註冊native方法

完整程式碼如下:

#include <jni.h>
#include <string>
#include <android/log.h>
#define LIBENC_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "mynative", __VA_ARGS__))
#define LIBENC_ARRAY_ELEMS(a)  (sizeof(a) / sizeof(a[0]))
static JavaVM *jvm;
static JNIEnv *jenv;
//native測試方法
static jstring  stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
//native測試方法
 jint  getTestMethod(
        JNIEnv *env,
        jobject,jstring str,jint a,jlong b,jdouble c) {
    return 123456;
}
/**
 * native方法集合
 */
static JNINativeMethod mynative_methods[] = {
        //java類中宣告的native方法名字   (引數,引數。。。)返回值型別             jni中對應的方法
        {"stringFromJNI",           "()Ljava/lang/String;",            (void *) stringFromJNI},
        {"getTestMethod",           "(Ljava/lang/String;IJD)I",            (void *) getTestMethod}
};


jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    jvm = vm;
    //獲取JNIEnv 指標
    if (jvm->GetEnv((void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
        LIBENC_LOGE("Env not got");
        return JNI_ERR;
    }
    //類似反射得到宣告native方法的路徑包名+類名
    jclass clz = jenv->FindClass("libyuv/aimissu/com/libyuvtestdemo/MainActivity");
    if (clz == NULL) {
        LIBENC_LOGE("Class \"libyuv/aimissu/com/libyuvtestdemo/MainActivity\" not found");
        return JNI_ERR;
    }
    //註冊
    if (jenv->RegisterNatives(clz, mynative_methods, LIBENC_ARRAY_ELEMS(mynative_methods))) {
        LIBENC_LOGE("methods not registered");
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}