Android Studio NDK JNI動態註冊本地方法

概述

可能你們以爲javah生成的函數名又臭又長,不太好看。這裏能夠提供另一種方法來動態註冊c++函數,讓其根Java中的native方法關聯起來。java

實現

這裏經過JNIEnv的Resisternatives方法完成方法的註冊。相關方法介紹:c++

//方法映射描述結構體
typedef struct {
    const char* name;//Java方法名
    const char* signature;//方法簽名
    void*       fnPtr;//C++ 方法指針
} JNINativeMethod;

//這是JNIEnv提供的註冊本地方法
//clazz:方法對應的class
//methods:對應的方法數組指針
//nMethods:有幾個方法
//返回值:註冊成功返回JNI_OK
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
        jint nMethods);

//當本地庫被加載時VM調用JNI_OnLoad(例如,經過系統調用LoadLibrary)。JNI_OnLoad必須返回由本地庫所需的JNI版本。
//爲了使用任何新的JNI函數,一個本地庫必須導出JNI_OnLoad函數並返回JNI_VERSION_1_2或更高的版本。
//若是本地庫不導出JNI_OnLoad功能,VM假定庫只須要JNI_VERSION_1_1版本。
//若是虛擬機不認JNI_OnLoad返回的版本號,本地庫不能加載。
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved);

看了上面的函數,咱們能夠知道在loadLibrary的時候會首先調用JNI_OnLoad。所以打算在JNI_OnLoad中完成方法註冊:數組

/filename:my.cpp
// Created by wastrel on 2016/9/8.
//
#include <stddef.h>
#include "jni.h"

//返回一個字符串
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz) {
    return env->NewStringUTF("Hello from C++");
}
//求兩個int的值
JNIEXPORT jint JNICALL native_add(JNIEnv *env, jobject object, jint a, jint b) {
    return a + b;
}
//方法數組,JNINativeMethod的第一個參數是Java中的方法名,第二個參數是函數簽名,第三個參數是對應的方法指針。
//Java方法的簽名必定要與對應的C++方法參數類型一致,不然註冊方法可能失敗。
static JNINativeMethod method_table[] = {
        {"native_hello", "()Ljava/lang/String;", (void *) native_hello},
        {"native_add",   "(II)I",                (void *) native_add}
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    //OnLoad方法是沒有JNIEnv參數的,須要經過vm獲取。
    JNIEnv *env = NULL;
    if (vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
       //獲取對應聲明native方法的Java類
      jclass  clazz = env->FindClass("com/example/registerjni/HelloJNI");
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        //註冊方法,成功返回正確的JNIVERSION。
        if (env->RegisterNatives(clazz, method_table, sizeof(method_table)/ sizeof(method_table[0]))==JNI_OK) {
            return JNI_VERSION_1_4;
        }
    }
    return JNI_FALSE;
}

對應的native聲明Java文件:函數

package com.example.registerjni;

/**
 * Created by wastrel on 2016/9/8.
 */
public class HelloJNI {
    static {
        System.loadLibrary("helloJNI");
    }

    public native static String native_hello();
    public native int native_add(int a,int b);
}
注意:若是你的方法聲明瞭static,那麼對應的第二個參數應該是jclass類型。若是你清楚他的實際類型,即使你寫成jobject也不會引發程序錯誤,由於jclass自己也是jobject。但仍是建議寫爲正確的方式,這樣能夠顯得清晰一些。

注意:C++和Java有所不一樣,若是把static JNINativeMethod method_table 寫在開頭,你編譯的時候會提示找不到函數指針,這是由於自上而下編譯的緣由。因此應該把定義寫在實現方法後面,或者用一個頭文件來完成函數的定義。

後記

這只是一種創建Native方法與Java方法的方式,若是沒有特殊的須要,不建議使用這種方法來註冊。由於使用這種優雅的註冊方法,你必須確保你的函數方法和簽名的正確性。這可能會增長出錯的機率。spa

相關文章
相關標籤/搜索