零基礎(chǔ)帶你吃掉JNI全家桶(一)

前言

大家好旭斥!我又來了垂券,這次準(zhǔn)備著手寫一個(gè)JNI開發(fā)系列,畢竟算芯,現(xiàn)在JNI開發(fā)也是在各個(gè)公司越來越重要了凳宙,如果項(xiàng)目畢竟大,可能涉及的模塊較多届囚,比如你作為應(yīng)用層的開發(fā)削葱,難免避免不了需要使用一些庫,一些加密操作等等昔字,一般都會(huì)放在本地方法里面首繁,比較安全弦疮,人家丟給你so文件或者靜態(tài)a文件。咏尝。你不會(huì)用豈不是很尷尬。網(wǎng)上資料比較雜胎食,而且很亂允懂,大部分還是在用.mk的方法,本系列就基于CMake形式粥航,希望能夠帶著一些希望學(xué)習(xí)JNI開發(fā)的小伙伴一起學(xué)會(huì)JNI開發(fā)~

零基礎(chǔ)帶你吃掉JNI全家桶(二)

零基礎(chǔ)帶你吃掉JNI全家桶(三)

從一個(gè)栗子說起

c++ support

注意:要支持CMake递雀,此時(shí)我們需要勾選 Include C++ support置侍,然后點(diǎn)擊Next--->Finish,完成工程的創(chuàng)建。
創(chuàng)建完成后,我們打開工程目錄,發(fā)現(xiàn)增加了幾個(gè)不一樣的地方:
image.png

發(fā)現(xiàn)AS已經(jīng)幫我們生產(chǎn)一個(gè)cpp目錄以及一個(gè)native-lib.cpp的c++文件秕衙,在根目錄下据忘,也多了一個(gè)CMakeLists.txt文件搞糕,我們主要來關(guān)注CMakeLists.txt里面的東東

#定義cmake支持的最小版本號(hào)
cmake_minimum_required(VERSION 3.4.1)


add_library( # 設(shè)置生成so庫的文件名稱,例如此處生成的so庫文件名稱應(yīng)該為:libnative-lib.so
             native-lib

             # 設(shè)置生成的so庫類型汉规,類型只包含兩種:
             # STATIC:靜態(tài)庫驹吮,為目標(biāo)文件的歸檔文件碟狞,在鏈接其他目標(biāo)的時(shí)候使用
             # SHARED:動(dòng)態(tài)庫,會(huì)被動(dòng)態(tài)鏈接频祝,在運(yùn)行時(shí)被加載
             SHARED

             # 設(shè)置源文件的位置,可以是很多個(gè)源文件沽一,都要添加進(jìn)來窟绷,注意相對(duì)位置
             src/main/cpp/native-lib.cpp )

# 從系統(tǒng)里查找依賴庫,可添加多個(gè)
find_library( # 例如查找系統(tǒng)中的log庫liblog.so
              log-lib

              # liblog.so庫指定的名稱即為log,如同上面指定生成的libnative-lib.so庫名稱為native-lib一樣
              log )
# 配置目標(biāo)庫的鏈接攘残,即相互依賴關(guān)系
target_link_libraries( # 目標(biāo)庫(最終生成的庫)
                       native-lib

                        # 依賴于log庫歼郭,一般情況下辐棒,如果依賴的是系統(tǒng)中的庫,需要加 ${} 進(jìn)行引用泰涂,
                        # 如果是第三方庫辐怕,可以直接引用庫名寄疏,例如:
                        # 引用第三方庫libthird.a,引用時(shí)直接寫成third;注意陕截,引用時(shí)农曲,每一行只能引用一個(gè)庫
                       ${log-lib} )

這里我把注釋進(jìn)行了縮減,標(biāo)注了中文注釋罚渐,比較詳細(xì)驯妄,不明白的可以看下每個(gè)的作用,當(dāng)然還有很多API可以使用青扔,后續(xù)再詳細(xì)說明,也可以看看官方文檔谈息,[戳我戳我]
(https://developer.android.google.cn/ndk/guides/cmake)

我們新建一個(gè)Helper類來編寫native方法

public class NativeHelper  {
    static {
        System.loadLibrary("native-lib");
    }
    public  native String stringFromJNI();
    public  native int add(int a,int b);
 
}

打開我們的MainActivity

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }
}

可以看到最上面侠仇,靜態(tài)代碼塊引用了native-lib這個(gè)庫逻炊,然后直接調(diào)用native本地方法,將C++中返回的字符串拿到進(jìn)行顯示豹休。然后看看C++具體是怎么是實(shí)現(xiàn)的

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring

JNICALL
Java_com_example_hik_cmake_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

代碼很簡單桨吊,引用兩個(gè)頭文件视乐,然后定義了一個(gè)方法,返回“Hello from C++”這個(gè)字符串佑淀,那有的朋友要問了渣聚,為什么java層直接調(diào)用stringFromJNI()方法能夠直接映射到C++里面的方法呢僧叉,細(xì)心的小伙伴可能發(fā)現(xiàn)了瓶堕,C++里面的這個(gè)方法名很長而且很熟悉。谭梗。這不是Java包名加上方法名拼湊而成的字符串嗎宛蚓,這種方式呢叫做靜態(tài)注冊(cè),這樣就能通過這個(gè)映射方式找到C++中的方法远舅。

有的朋友又要說了,這么長方法名也太麻煩了序六,雖然可以自動(dòng)生成蚤吹,但是多不美觀裁着,多不優(yōu)雅。爆土。是滴诸蚕!有靜態(tài)注冊(cè)背犯,那當(dāng)然就有動(dòng)態(tài)注冊(cè)了~我們來改一改代碼:

#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG "JNI_"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)
JNICALL
jstring backStringToJava(JNIEnv *env, jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
//動(dòng)態(tài)注冊(cè)
jint registerMethod(JNIEnv *env) {
    jclass clz = env->FindClass("com/example/taolin/jni_project/NativeHelper");
    if (clz == NULL) {
        LOGD("con't find class: com/example/taolin/jni_project/NativeHelper");
    }
    JNINativeMethod jniNativeMethod[] = {{"stringFromJNI", "()Ljava/lang/String;", (void *) backStringToJava}};
    return env->RegisterNatives(clz,jniNativeMethod, sizeof(jniNativeMethod)/ sizeof(jniNativeMethod[0]));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved){
    JNIEnv * env = NULL;
    if (vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){
        return JNI_ERR;
    }
    jint result = registerMethod(env);
    LOGD("RegisterNatives result: %d", result);
    return JNI_VERSION_1_6;
}

這里呢漠魏,為了在C++中打印日志,我們需要引入log.h頭文件哪自,然后我們把之前的方法名改成backStringToJava禁熏,然后因?yàn)闆]有了靜態(tài)注冊(cè)的規(guī)則瞧毙,Java層調(diào)用的使用當(dāng)然就找不到我們對(duì)應(yīng)的方法了,我們定義一個(gè)動(dòng)態(tài)注冊(cè)的方法矩动,將Java中的方法和C++中的方法進(jìn)行動(dòng)態(tài)的綁定:

  • 通過env指針释漆,拿到MainActivity的class對(duì)象灵汪,具體env指針后續(xù)會(huì)詳細(xì)說明
  • 定義一系列的方法對(duì)象柑潦,每個(gè)包含三個(gè)參數(shù)渗鬼,第一個(gè)是java中的方法名荧琼,第二個(gè)是方法對(duì)應(yīng)的簽名,第三個(gè)是C++中的方法名
  • 在JNI_OnLoad方法中堰乔,調(diào)用動(dòng)態(tài)注冊(cè)綁定方法進(jìn)行綁定

有些朋友可能對(duì)方法簽名不太明白脐恩,后續(xù)語法會(huì)詳細(xì)說明驶冒,這里先簡單說下,方法簽名也就是一個(gè)方法唯一性的一個(gè)標(biāo)準(zhǔn)崇猫,上面的()Ljava/lang/String;就是stringFromJNI的簽名需忿,前面的括號(hào)里面是參數(shù)的簽名屋厘,因?yàn)檫@里沒有參數(shù),所以為空澈魄,緊接著后面是返回值得簽名仲翎,規(guī)則是溯香,如果是基本數(shù)據(jù)類型就是相對(duì)應(yīng)的基本數(shù)據(jù)類型浓恶,如果不是基本數(shù)據(jù)類型,那么就是L+對(duì)象包名+“;”湿镀,注意這里的分號(hào)不可省略C愠铡!根據(jù)這個(gè)規(guī)則瀑罗,下面那個(gè)方法的簽名就是(II)I雏掠,依次類推乡话,沒明白的也沒關(guān)系,后面會(huì)詳細(xì)對(duì)JNI中的語法詳細(xì)解釋诬像,先知道有這么回事就好了时迫。

public native String stringFromJNI(); 
//簽名:()Ljava/lang/String;
public native int add(int a int b) 
//簽名:"(II)I"

然后我們直接運(yùn)行APP掠拳,可以發(fā)現(xiàn)頁面上顯示出來了“Hello from C++”字符串,然后看看我們生成的so文件在哪:


image.png

OK喊熟!大功告成芥牌,我們的第一步就完成了聂使,成功的完成了Java調(diào)用C++的方法,但別高興的太早弃理,這只是第一步痘昌,好了,看到這的獎(jiǎng)勵(lì)自己跟辣條吧~算灸,溜了溜了驻啤。街佑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沐旨,一起剝皮案震驚了整個(gè)濱河市森逮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磁携,老刑警劉巖褒侧,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谊迄,居然都是意外死亡闷供,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門统诺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歪脏,“玉大人,你說我怎么就攤上這事粮呢。” “怎么了啄寡?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挺物。 經(jīng)常有香客問我懒浮,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任惰匙,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扣囊。我一直安慰自己,他們只是感情好侵歇,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惕虑,像睡著了一般坟冲。 火紅的嫁衣襯著肌膚如雪溃蔫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天伟叛,我揣著相機(jī)與錄音私痹,去河邊找鬼。 笑死统刮,一個(gè)胖子當(dāng)著我的面吹牛紊遵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侥蒙,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼暗膜,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了鞭衩?” 一聲冷哼從身側(cè)響起学搜,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎醋旦,沒想到半個(gè)月后恒水,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饲齐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年钉凌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捂人。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡御雕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滥搭,到底是詐尸還是另有隱情酸纲,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布瑟匆,位于F島的核電站闽坡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疾嗅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一外厂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧代承,春花似錦汁蝶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膀估,卻和暖如春幔亥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玖像。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工紫谷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捐寥。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓笤昨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親握恳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞒窒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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