Android JNI 函數(shù)注冊的兩種方式(靜態(tài)注冊/動態(tài)注冊)

JNI/NDK

在Android開發(fā)中豁护,由于種種原因我們需要調(diào)用C/C++代碼, 這個時候就要用到Android開發(fā)者都聽說過的JNI(Java Native Interface)了, 在調(diào)用JNI相關(guān)方法之前, 要對java中native關(guān)鍵字定義的方法進(jìn)行注冊, 注冊方式有兩種: 靜態(tài)注冊和動態(tài)注冊, 兩者優(yōu)缺點如下:

  • 靜態(tài)注冊
    優(yōu)點: 理解和使用方式簡單, 屬于傻瓜式操作, 使用相關(guān)工具按流程操作就行, 出錯率低
    缺點: 當(dāng)需要更改類名,包名或者方法時, 需要按照之前方法重新生成頭文件, 靈活性不高
  • 動態(tài)注冊
    優(yōu)點: 靈活性高, 更改類名,包名或方法時, 只需對更改模塊進(jìn)行少量修改, 效率高
    缺點: 對新手來說稍微有點難理解, 同時會由于搞錯簽名, 方法, 導(dǎo)致注冊失敗

靜態(tài)注冊

此注冊方法是初學(xué)者經(jīng)常用到的, 比較常見, 這里簡單說下流程,
1.編寫一個java類,在里面加載對應(yīng)的so庫并且通過native關(guān)鍵字定義需要調(diào)用的函數(shù)

package com.example.wenzhe.myjni;
/**
 * Created by wenzhe on 16-1-27.
 */
public class JniTest {
public native int getRandomNum();
public native String getNativeString();

static {
    System.loadLibrary("HelloJni");
    }
}

2.在命令行下輸入 javac JniTest.java 生成JniTest.class文件
然后在src目錄下通過 javah com.example.wenzhe.myjni.JniTest 生成 com_example_wenzhe_myjni_JniTest.h 頭文件
3.將頭文件拷貝到j(luò)ni目錄下(eclipse在src同級目錄建立文件夾酗昼,Android studio 在java同級目錄建立文件夾)
4.編寫C/C++源代碼 并把剛拷貝的頭文件包含進(jìn)去 ,復(fù)制頭文件中函數(shù)的定義部分,并實現(xiàn)其中的你想要的功能

然后編寫Android.mk Application.mk(Application.mk主要用來定義適應(yīng)的平臺综看,x86 arm等)

Android.mk如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := HelloJni
LOCAL_SRC_FILES := HelloJni.cpp

include $(BUILD_SHARED_LIBRARY)

Application.mk如下:

#支持標(biāo)準(zhǔn)C++特性
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
#支持的CPU架構(gòu)
APP_ABI := armeabi-v7a
#Android 版本
APP_PLATFORM := android-22

include $(BUILD_SHARED_LIBRARY)

其中LOCAL_MODULE定義的名字就是生成的so庫名字挂滓,so庫前面都會有個lib前綴,上面生產(chǎn)的so應(yīng)該為 libHelloJni.so

5.在命令行中進(jìn)入jni目錄姻灶,輸入ndk-build 即可生產(chǎn)對應(yīng)so庫铛绰,會自動放在libs文件夾下 至此就可以運行程序了

動態(tài)注冊

動態(tài)注冊基本思想是在JNI_Onload()函數(shù)中通過JNI中提供的RegisterNatives()方法來將C/C++方法和java方法對應(yīng)起來(注冊), 我們在調(diào)用 System.loadLibrary的時候,會在C/C++文件中回調(diào)一個名為 JNI_OnLoad ()的函數(shù),在這個函數(shù)中一般是做一些初始化相關(guān)操作, 我們可以在這個方法里面注冊函數(shù), 注冊整體流程如下:

  1. 編寫Java端的相關(guān)native方法
  2. 編寫C/C++代碼, 實現(xiàn)JNI_Onload()方法
  3. 將Java 方法和 C/C++方法通過簽名信息一一對應(yīng)起來
  4. 通過JavaVM獲取JNIEnv, JNIEnv主要用于獲取Java類和調(diào)用一些JNI提供的方法
  5. 使用類名和對應(yīng)起來的方法作為參數(shù), 調(diào)用JNI提供的函數(shù)RegisterNatives()注冊方法

示例代碼如下:

// jni頭文件 
#include <jni.h>
 
#include <cassert>
#include <cstdlib>
#include <iostream>
using namespace std;
 
 
//native 方法實現(xiàn)
jint get_random_num(){
    return rand();
}
/*需要注冊的函數(shù)列表,放在JNINativeMethod 類型的數(shù)組中产喉,
以后如果需要增加函數(shù)捂掰,只需在這里添加就行了
參數(shù):
1.java中用native關(guān)鍵字聲明的函數(shù)名
2.簽名(傳進(jìn)來參數(shù)類型和返回值類型的說明) 
3.C/C++中對應(yīng)函數(shù)的函數(shù)名(地址)
*/
static JNINativeMethod getMethods[] = {
        {"getRandomNum","()I",(void*)get_random_num},
};
//此函數(shù)通過調(diào)用RegisterNatives方法來注冊我們的函數(shù)
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
    jclass clazz;
    //找到聲明native方法的類
    clazz = env->FindClass(className);
    if(clazz == NULL){
        return JNI_FALSE;
    }
   //注冊函數(shù) 參數(shù):java類 所要注冊的函數(shù)數(shù)組 注冊函數(shù)的個數(shù)
    if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
        return JNI_FALSE;
    }
    return JNI_TRUE;
}
 
static int registerNatives(JNIEnv* env){
    //指定類的路徑,通過FindClass 方法來找到對應(yīng)的類
    const char* className  = "com/example/wenzhe/myjni/JniTest";
    return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}
//回調(diào)函數(shù)
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
   //獲取JNIEnv
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    //注冊函數(shù) registerNatives ->registerNativeMethods ->env->RegisterNatives
    if(!registerNatives(env)){
        return -1;
    }
    //返回jni 的版本 
    return JNI_VERSION_1_6;
}

上面的代碼就能實現(xiàn)動態(tài)注冊JNI了 以后要增加函數(shù)只需在java文件中聲明native方法曾沈,在C/C++文件中實現(xiàn)这嚣,
并在getMethods數(shù)組添加一個元素并指明對應(yīng)關(guān)系,通過ndk-build 生成so庫就可以運行了
其中JNI版本可以在jni.h頭文件中去查看支持哪些版本塞俱,一般定義在文件最后幾行

JNI 簽名

動態(tài)注冊中 JNINativeMethod 結(jié)構(gòu)體中第二個參數(shù)需注意
括號內(nèi)代表傳入?yún)?shù)的簽名符號姐帚,為空可以不寫,括號外代表返回參數(shù)的簽名符號,為空填寫 V障涯,對應(yīng)關(guān)系入下表

簽名符號 C/C++ java
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
[Z jbooleanArray boolean[]
[I jintArray int[]
[J jlongArray long[]
[D jdoubleArray double[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
L完整包名加類名; jobject class

舉個例子:

傳入的java參數(shù)有兩個 分別是 int 和 long[] 函數(shù)返回值為 String 即函數(shù)的定義為:String getString(int a ,long[] b)
簽名就應(yīng)該是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分號)
如果有內(nèi)部類 則用 $ 來分隔 如:Landroid/os/FileUtils$FileStatus;

總結(jié)

當(dāng)熟悉動態(tài)注冊后, 動態(tài)注冊無疑是注冊函數(shù)的更好方式, 唯一要注意的是注冊函數(shù)時, 需要額外小心, 別把類名,函數(shù)名和簽名寫錯了, 不然loadLibraries時會導(dǎo)致應(yīng)用Crash, 關(guān)于JNI部分知識, 我還會寫一篇關(guān)于如何高效傳遞數(shù)據(jù)以及JNI開發(fā)過程中的一些坑的總結(jié).

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罐旗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子像樊,更是在濱河造成了極大的恐慌尤莺,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件生棍,死亡現(xiàn)場離奇詭異颤霎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)涂滴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門友酱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柔纵,你說我怎么就攤上這事缔杉。” “怎么了搁料?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵或详,是天一觀的道長系羞。 經(jīng)常有香客問我,道長霸琴,這世上最難降的妖魔是什么椒振? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮梧乘,結(jié)果婚禮上澎迎,老公的妹妹穿的比我還像新娘。我一直安慰自己选调,他們只是感情好夹供,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仁堪,像睡著了一般哮洽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弦聂,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天袁铐,我揣著相機(jī)與錄音,去河邊找鬼横浑。 笑死,一個胖子當(dāng)著我的面吹牛屉更,可吹牛的內(nèi)容都是我干的徙融。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瑰谜,長吁一口氣:“原來是場噩夢啊……” “哼欺冀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萨脑,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤隐轩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后渤早,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體职车,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年鹊杖,在試婚紗的時候發(fā)現(xiàn)自己被綠了悴灵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡骂蓖,死狀恐怖积瞒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情登下,我是刑警寧澤茫孔,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布叮喳,位于F島的核電站,受9級特大地震影響缰贝,放射性物質(zhì)發(fā)生泄漏馍悟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一揩瞪、第九天 我趴在偏房一處隱蔽的房頂上張望赋朦。 院中可真熱鬧,春花似錦李破、人聲如沸宠哄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毛嫉。三九已至,卻和暖如春妇菱,著一層夾襖步出監(jiān)牢的瞬間承粤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工闯团, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留辛臊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓房交,卻偏偏與公主長得像彻舰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子候味,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容