Android JNI開發(fā)詳解(2)-函數(shù)注冊(cè)

1. JNI開發(fā)流程

  1. 創(chuàng)建Native C++工程,這部分可用參考[Android JNI開發(fā)詳解(2)-開發(fā)環(huán)境搭建](Android JNI開發(fā)工具篇(1)-開發(fā)環(huán)境搭建.md)

  2. 創(chuàng)建Java層本地接口調(diào)用類漾稀,并定義好相應(yīng)的本地函數(shù)。

  3. 將Java源代碼編譯成class字節(jié)碼文件(Android studio會(huì)自動(dòng)生成)粒梦。

  4. 創(chuàng)建對(duì)應(yīng)的本地函數(shù)接口,并注冊(cè)本地函數(shù)重抖,Jni函數(shù)注冊(cè)有兩種方式,靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè),靜態(tài)注冊(cè)使用javah工具自動(dòng)生成對(duì)應(yīng)的本地接口函數(shù)定義,動(dòng)態(tài)注冊(cè)則需要在JNI_OnLoad函數(shù)中調(diào)用RegisterNatives來完成注冊(cè)兔综。

  5. 實(shí)現(xiàn)本地函數(shù)接口函數(shù)的具體功能實(shí)現(xiàn)。

  6. 修改CMakeLists.txtAndroid.mk慧瘤。

  7. 編譯測(cè)試伐坏。

2. JNI函數(shù)注冊(cè)

2.1 JVM查找native方法有兩種方式

JNI技術(shù)是Java世界與Native世界的通信橋梁独泞,具體到代碼,Java層的代碼如何同Native層的代碼進(jìn)行調(diào)用的呢?我們都知道淘菩,在調(diào)用native方法之前汇在,首先要調(diào)用System.loadLibrary接口加載一個(gè)實(shí)現(xiàn)了native方法的動(dòng)態(tài)庫才能正常訪問殖告,否則就會(huì)拋出java.lang.UnsatisfiedLinkError異常 。 那么黄绩,在Java中調(diào)用某個(gè)native方法時(shí),JVM是通過什么方式筑煮,能正確的找到動(dòng)態(tài)庫中C/C++實(shí)現(xiàn)的那個(gè)native函數(shù)呢习劫?

JVM查找native方法有兩種方式:

  1. 按照J(rèn)NI規(guī)范的命名規(guī)則。

  2. 調(diào)用JNI提供的RegisterNatives函數(shù)谤狡,將本地函數(shù)注冊(cè)到JVM中墓懂。

第一種方式,可用使用javah工具按照J(rèn)ava類中定義的native方法础浮,按照J(rèn)NI規(guī)范的命名規(guī)則的方式自動(dòng)生成Jni本地C/C++頭文件撵枢。第二種方式則需要在本地庫的JNI_OnLoad函數(shù)中調(diào)用RegisterNatives來動(dòng)態(tài)注冊(cè)精居。

JNI函數(shù)注冊(cè)是將Java層聲明的Native方法同實(shí)際的Native函數(shù)綁定起來的實(shí)現(xiàn)方式,也就是說沃但,只要通過JNI函數(shù)注冊(cè)機(jī)制注冊(cè)了本地方维雇,Java層就可以直接調(diào)用定義的這些本地方法了。對(duì)應(yīng)上述JVM查找native方法的兩種方式逸贾,JNI函數(shù)注冊(cè)方式一般分為靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)兩種方式津滞。

2.2 javah工具和javap工具

為了方便我們進(jìn)行完成注冊(cè)操作,我們經(jīng)常還會(huì)用到j(luò)avah和javap兩個(gè)命令行工具咪鲜。

  • javah工具用于生產(chǎn)本地方法頭文件

    在JDK1.7中撞鹉,在src目錄(android studio工程在src/main/java目錄)下執(zhí)行javah 全類名

    在JDK1.6中颖侄,在bin/classes目錄下執(zhí)行

    執(zhí)行前需要先編譯通過(編譯為class文件)隆敢,大多數(shù)IDE會(huì)自動(dòng)執(zhí)行編譯,因此不需要在手動(dòng)進(jìn)行編譯

  • javap工具用于打印方法簽名

2.3 靜態(tài)注冊(cè)

靜態(tài)注冊(cè)實(shí)際十分簡(jiǎn)單穴墅,就是使用上面提到的javah工具為我們生產(chǎn)本地方法頭文件温自,然后在根據(jù)生成的頭文件完成相應(yīng)的函數(shù)即可。靜態(tài)注冊(cè)即本地函數(shù)按照特定的命名規(guī)則命名本地方法松捉,使得這些本地方法與Java類中定義的本地方法一一對(duì)應(yīng)馆里,在執(zhí)行JNI調(diào)用時(shí),只需要根據(jù)命名規(guī)則去調(diào)用so庫中對(duì)應(yīng)的方法即可丙者。

下面時(shí)一個(gè)使用靜態(tài)注冊(cè)的例子营密,Test類定義了Java中的本地方法。

package cc.ccbu.jnitest;

public class Test {
    static {
        System.loadLibrary("native-lib");
    }

    public native String textFromJni();
}

使用javah生成對(duì)應(yīng)的本地方法頭文件纷捞。

#include <jni.h>
/* Header for class cc_ccbu_jnitest_Test */

#ifndef _Included_cc_ccbu_jnitest_Test
#define _Included_cc_ccbu_jnitest_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     cc_ccbu_jnitest_Test
 * Method:    textFromJni
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_cc_ccbu_jnitest_Test_textFromJni
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif

我們只用在我們的.c或cpp文件實(shí)現(xiàn)Java_cc_ccbu_jnitest_Test_textFromJni方法即可主儡。

2.4 動(dòng)態(tài)注冊(cè)

對(duì)應(yīng)與上面的靜態(tài)注冊(cè)方法惨缆,還有一種動(dòng)態(tài)注冊(cè)JNI函數(shù)的方式,即動(dòng)態(tài)注冊(cè)踪央。動(dòng)態(tài)注冊(cè)是當(dāng)Java層調(diào)用System.loadLibrary方法加載so庫后瓢阴,本地庫的JNI_OnLoad函數(shù)會(huì)被調(diào)用荣恐,在JNI_OnLoad函數(shù)中通過調(diào)用RegisterNatives函數(shù)來完成本地方法的注冊(cè)累贤。

本地so庫加載和卸載時(shí)少漆,會(huì)調(diào)用JNI_OnLoad和JNI_OnUnload函數(shù),函數(shù)原型如下:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);

動(dòng)態(tài)注冊(cè)本地方法的函數(shù)RegisterNatives原型如下:

jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);

其中JNINativeMethod結(jié)構(gòu)體用來描述本地方法結(jié)構(gòu)渗磅,其定義如下:

typedef struct {
    const char* name;      // Java方法名
    const char* signature; // Java方法簽名
    void* fnPtr;           // jni本地方法對(duì)應(yīng)的函數(shù)指針
} JNINativeMethod;

下面通過一個(gè)例子來展示jni動(dòng)態(tài)注冊(cè)本地方法的過程检访。還是以上面靜態(tài)注冊(cè)的Test類為例

  1. 在Java文件中定義本地方法,加載本地so庫

    package cc.ccbu.jnitest;
    
    public class Test {
        static {
            System.loadLibrary("native-lib");
        }
    
        public native String textFromJni();
    }
    
  2. 在JNI_OnLoad函數(shù)中注冊(cè)本地方法

    jstring textFromJni(JNIEnv* env, jobject thiz) {
        return env->NewStringUTF("text from jni");
    }
    
    static JNINativeMethod gMethods[] = {
            {"textFromJni", "()Ljava/lang/String;", (void*)textFromJni}
    };
    
    int registerMethod(JNIEnv *env) {
        jclass test = env->FindClass("cc/ccbu/jnitest/Test");
        return env->RegisterNatives(test, gMethods, sizeof(gMethods)/ sizeof(gMethods[0]));
    }
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
        JNIEnv* env = NULL;
        if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
            return JNI_ERR;
        }
    
        if (registerMethod(env) != JNI_OK) {
            return JNI_ERR;
        }
    
        return  JNI_VERSION_1_6;
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市会烙,隨后出現(xiàn)的幾起案子筒捺,更是在濱河造成了極大的恐慌,老刑警劉巖葫盼,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件村斟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡孩灯,警方通過查閱死者的電腦和手機(jī)逾滥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門寨昙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舔哪,你說我怎么就攤上這事√浚” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵豌拙,是天一觀的道長(zhǎng)题暖。 經(jīng)常有香客問我,道長(zhǎng)逞敷,這世上最難降的妖魔是什么灌侣? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任侧啼,我火速辦了婚禮,結(jié)果婚禮上痊乾,老公的妹妹穿的比我還像新娘。我一直安慰自己蛾魄,他們只是感情好湿滓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布叽奥。 她就那樣靜靜地躺著,像睡著了一般朝氓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上待德,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天枫夺,我揣著相機(jī)與錄音,去河邊找鬼涧偷。 笑死毙死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的确封。 我是一名探鬼主播再菊,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秉剑!你這毒婦竟也來了稠诲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤略水,失蹤者是張志新(化名)和其女友劉穎劝萤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跨释,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡煤傍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年嘱蛋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片龄恋。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凶伙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出显押,到底是詐尸還是另有隱情,我是刑警寧澤挖息,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布兽肤,位于F島的核電站,受9級(jí)特大地震影響电禀,放射性物質(zhì)發(fā)生泄漏笤休。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一葫松、第九天 我趴在偏房一處隱蔽的房頂上張望底洗。 院中可真熱鬧,春花似錦亥揖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滑负。三九已至,卻和暖如春矮慕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘟斜。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虽惭,地道東北人蛇尚。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亲雪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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