NDK開發(fā) 從入門到放棄(一:基本流程入門了解)

一刃唐、前言

● NDK
Native Development Kit(NDK)是一系列工具的集合。它提供了一系列的工具界轩,幫助開發(fā)者快速開發(fā)C/C++的動態(tài)庫画饥,并能自動將so和java一起打包成apk。

● JNI
Java Native Interface(JNI)標(biāo)準(zhǔn)是java平臺的一部分浊猾,JNI是Java語言提供的Java和C/C++相互溝通的機(jī)制抖甘,Java可以通過JNI調(diào)用C/C++代碼,C/C++的代碼也可以調(diào)用java代碼葫慎。

● JNI與NDK的關(guān)系
NDK可以為我們生成了C/C++的動態(tài)鏈接庫衔彻,JNI是java和C/C++溝通的接口,兩者與android沒有半毛錢關(guān)系偷办,只因?yàn)榘沧渴莏ava程序語言開發(fā)艰额,然后通過JNI又能與C/C++溝通,所以我們可以使用NDK+JNI來實(shí)現(xiàn)“Java+C”的開發(fā)方式椒涯。

● 為什么要NDK開發(fā)

NDK開發(fā)具有以下優(yōu)點(diǎn):

  1. 項目需要調(diào)用底層的一些C/C++的一些東西(java無法直接訪問到操作系統(tǒng)底層(如系統(tǒng)硬件等))柄沮,或者已經(jīng)在C/C++環(huán)境下實(shí)現(xiàn)了功能代碼(大部分現(xiàn)存的開源庫都是用C/C++代碼編寫的。)废岂,直接使用即可湖苞。NDK開發(fā)常用于驅(qū)動開發(fā)、無線熱點(diǎn)共享哈扮、數(shù)學(xué)運(yùn)算蚓再、實(shí)時渲染的游戲摘仅、音視頻處理问畅、文件壓縮、人臉識別矾端、圖片處理等。
  2. 為了效率更加高效些砚亭。將要求高性能的應(yīng)用邏輯使用C/C++開發(fā)殴玛,從而提高應(yīng)用程序的執(zhí)行效率滚粟。但是C/C++代碼雖然是高效的,在java與C/C++相互調(diào)用時卻增大了開銷署尤;
  3. 基于安全性的考慮鲤遥。防止代碼被反編譯盖奈,為了安全起見,使用C/C++語言來編寫重要的部分以增大系統(tǒng)的安全性究孕,最后生成so庫(用過第三方庫的應(yīng)該都不陌生)便于給人提供方便厨诸。(任何有效的代碼混淆對于會smail語法反編譯你apk是分分鐘的事禾酱,即使你加殼也不能幸免高手的攻擊)
  4. 便于移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用颗管。

二垦江、安裝與配置

首先我們在Android Studio下新建一個安卓項目比吭。然后打開Project Structure界面,如下:

在SDK Location目錄下衩藤,有SDK和NDK的路徑,而這里我們暫時還未下載配置過NDK娄蔼,故我們需要點(diǎn)擊Download Android NDK來進(jìn)行下載(Android Studio還是很強(qiáng)大的岁诉,相比Eclipse能省不少事)跋选。這里Android Studio會下載最新版本的NDK進(jìn)行安裝前标,默認(rèn)會下載保存在SDK的路徑下炼列。我們在上圖中還能看到有一段介紹文字,說SDK以及NDK的路徑配置會保存在local.properties文件內(nèi)氢惋,安裝完成后我們刷新Project稽犁,進(jìn)local.properties文件查看也能看到SDK與NDK的路徑已亥。

NDK下載配置完成之后虑椎,需要在gradle.properties文件中加上一行:

android.useDeprecatedNdk=true
1
接下來,我們借助強(qiáng)大的Android Studio的插件功能传趾,在External Tools下配置兩個非常有用的插件墨缘。進(jìn)入Settings–>Tools–>ExternalTools零抬,點(diǎn)擊+號增加镊讼。

javah -jni命令蝶棋,是根據(jù)java文件生成.h頭文件的玩裙,會自動根據(jù)java文件中的類名(包含包名)與方法名生成對應(yīng)的C/C++里面的方法名吃溅。下面是參數(shù)配置及其含義:

  1. Program: JDKPath\bin\javah.exe 這里配置的是JDK目錄下的javah.exe的路徑决侈。
  2. Parametes: -classpath . -jni -d ModuleFileDir/src/main/jni FileClass 這里FileClass指的是要執(zhí)行操作的類名(即我們操作的文件)赖歌,ModuleFileDir/src/main/jni表示生成的文件保存在這個module目錄的src/main/jni目錄下庐冯。
  3. Working: ModuleFileDir\src\main\java module目錄下的src\main\java目錄(不是很理解)坎穿。
    使用方式:選中java文件—>右鍵—>External Tools—>javah-jni玲昧,將生成jni文件夾以及文件夾下的 包名.類名的.h頭文件 (名字過長酌呆,我們可以自己重命名)。

ndk -build命令痰娱,是根據(jù)C/C++文件生成so文件的梨睁。下面是參數(shù)配置及其含義:

  1. Program: F:\apk\sdk\ndk-bundle\ndk-build.cmd 這里配置的是ndk下的ndk-build.cmd的路徑(根據(jù)實(shí)際情況填寫)坡贺。
  2. Working: ModuleFileDir\src\main
    使用方式:選中C/C++文件—>右鍵—>ExternalTools—>ndk-build遍坟,將在main文件夾下生成libs文件夾以及多個so文件愿伴,我們可以移動至jniLibs目錄下去。

三鹅经、簡單實(shí)例

接下來我們創(chuàng)建一個訪問本地C/C++方法的java類瘾晃。

public class JniTest {
    /**
     * 將用C++代碼實(shí)現(xiàn)幻妓,在android代碼中調(diào)用的方法:獲取當(dāng)前app的包名
     * @param o
     * @return
     */
    public static native String getPackname(Object o);

    /**
     * 加載so庫或jni庫涌哲,在使用到該庫之前加載就行,不一定非要寫在這個類內(nèi)
     * 系統(tǒng)自己會判斷擴(kuò)展名是dll還是so,這里加載libJNI_ANDROID_TEST.so
     */
    static {
        System.loadLibrary("JNI_ANDROID_TEST");
    }
}

注意JNI_ANDROID_TEST這個Library名字哪廓,之后還會需要用到涡真,要保持一致哆料。該類提供了一個static的native方法东亦,該方法將用來獲取app的包名典阵。然后對該文件執(zhí)行javah -jni操作壮啊,生成對應(yīng)的.h頭文件歹啼。

如圖,已經(jīng)根據(jù)我們的java類生成了對應(yīng)的.h文件藤树,文件名為包名類名.h,我們可以手動改名為jnitest.h司志,里面只有一個方法降宅,返回值為String(jstring)骂远,方法名為Java類的包名類名方法名(包名中的分級不是用.而是_),前面兩個參數(shù)是C++里面必須有的(JNIEnv代表指向JVM的指針腰根,jclass是調(diào)用該方法的java對象)激才,第三個就是我們java類的方法里面的參數(shù)Object。注意额嘿,這是java函數(shù)與C++函數(shù)對應(yīng)的靜態(tài)注冊方法瘸恼,即通過特定的規(guī)則來寫,此處方法名可以隨意起名字册养,然后還可以用動態(tài)注冊的方式關(guān)聯(lián)兩個方法(顯然东帅,靜態(tài)注冊要簡單一些)。
然后我們新建一個C++文件球拦,取名為jnitest.cpp靠闭,寫上需要include的文件坎炼,從.h文件中復(fù)制方法過來(方法名、參數(shù)類型狼钮、返回值等必須一致!血與淚的教訓(xùn))。

至此半火,.h文件和c++文件均已完成酌住,接下來還需要在這個jni目錄下增加兩個文件,Android.mk和Application.mk。
Android.mk,注意LOCAL_MODULE的值與之前的名字相對應(yīng)怒炸,LOCAL_SRC_FILES的值寫c++文件的名字教寂,這兩個值成對設(shè)置迂烁,可設(shè)置多組库说。(:=是賦值的意思狰域,$是引用某變量的值。)

里面的符號正確的應(yīng)該是:=子巾,代碼中已更正缠导,圖片里面的更換麻煩就沒改了孩饼。很奇怪揪罕,我當(dāng)初寫的時候編譯運(yùn)行好像是沒出錯是正常的…(Tips.20170519)

LOCAL_PATH := $(call my-dir)     // 設(shè)置當(dāng)前的編譯目錄(Android.mk所在的目錄) 

include $(CLEAR_VARS)            // 清除LOCAL_XX變量(LOCAL_PATH除外)
LOCAL_MODULE := JNI_ANDROID_TEST  // 指定當(dāng)前編譯模塊的名稱  
LOCAL_SRC_FILES := jnitest.cpp    // 編譯模塊需要的源文件
include $(BUILD_SHARED_LIBRARY) // 指定編譯出的庫類型鳄抒,BUILD_SHARED_LIBRARY:動態(tài)庫贤重;BUILD_STATIC_LIBRARY:靜態(tài)庫, BUILD_EXECUTEABLE指:可執(zhí)行文件

在一個Android.mk文件中配置多個Module的方式如下(include$(CLEAR_VARS)、include $(BUILD_SHARED_LIBRARY)兩個語句也需要加上):

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := JNI_STATIC_ANDROID_TEST
LOCAL_SRC_FILES := jnistaticutils.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := JNI_DYNAMIC_ANDROID_TEST
LOCAL_SRC_FILES := jnidynamicutils.cpp
include $(BUILD_SHARED_LIBRARY)

Application.mk镰吵,APP_ABI有四種類型(默認(rèn)armeabi)戏售,armeabi、armeabi-v7a、x86炉奴、mips共耍,設(shè)置時以空格隔開字旭,all表示所有拍柒。該文件中有個可選配置的APP_MODULES屈暗,類似于上面Android.mk文件中的LOCAL_MODULE,以空格隔開淆攻,且會覆蓋掉Android.mk文件中的LOCAL_MODULE設(shè)置(比如Android.mk文件中的寫了兩個jni庫的配置艰毒,LOCAL_MODULE := JNI1、LOCAL_MODULE := JNI2获雕,而Application.mk中設(shè)置的APP_MODULES := JNI1童漩,則只能生成JNI1的so文件弄贿,要生成JNI2的so文件的時候會報錯,除非寫成APP_MODULES := JNI1 JNI2矫膨,這里我們直接省略默認(rèn)使用Android.mk中的)挎春。

APP_ABI := all
1
接下來我們需要對C++文件執(zhí)行ndk-build操作,生成相應(yīng)的so文件豆拨。

如圖直奋,在main/libs目錄下生成了多個so文件,名字為lib+我們指定的庫名(同時還生成了obj文件夾施禾,不知是什么東西)脚线。
這時候我們可以在main目錄下新建jniLibs文件夾,把生成的libs文件夾內(nèi)的東西均復(fù)制過去弥搞,刪除新生成的jni邮绿、libs、obj三個文件夾攀例。然后在Activity中測試調(diào)用船逮,在TextView上顯示我們通過C++代碼實(shí)現(xiàn)的方法getPackname獲取app的包名了。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.tv_app_package_name);
        tv.setText("packageName: " + JniTest.getPackname(MainActivity.this));
    }
}

測試能正確得到包名粤铭,說明調(diào)用成功了挖胃。我們可以把JniTest類以及so文件給別人去使用,這樣別人是看不到我們的代碼實(shí)現(xiàn)的梆惯,能很好的保護(hù)我們的源碼酱鸭。
B站學(xué)習(xí)鏈接:https://www.bilibili.com/video/BV11S4y1d7Dg/
歡迎大家一起交流學(xué)習(xí),可以給博主點(diǎn)個關(guān)注哦垛吗,及時看到消息凹髓。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市怯屉,隨后出現(xiàn)的幾起案子蔚舀,更是在濱河造成了極大的恐慌,老刑警劉巖锨络,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赌躺,死亡現(xiàn)場離奇詭異,居然都是意外死亡足删,警方通過查閱死者的電腦和手機(jī)寿谴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來失受,“玉大人讶泰,你說我怎么就攤上這事咏瑟。” “怎么了痪署?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵码泞,是天一觀的道長。 經(jīng)常有香客問我狼犯,道長余寥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任悯森,我火速辦了婚禮宋舷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓢姻。我一直安慰自己祝蝠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布幻碱。 她就那樣靜靜地躺著绎狭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褥傍。 梳的紋絲不亂的頭發(fā)上儡嘶,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音恍风,去河邊找鬼蹦狂。 笑死,一個胖子當(dāng)著我的面吹牛邻耕,可吹牛的內(nèi)容都是我干的鸥咖。 我是一名探鬼主播燕鸽,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼兄世,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了啊研?” 一聲冷哼從身側(cè)響起御滩,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎党远,沒想到半個月后削解,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沟娱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年氛驮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片济似。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡矫废,死狀恐怖盏缤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓖扑,我是刑警寧澤唉铜,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站律杠,受9級特大地震影響潭流,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柜去,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一灰嫉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗓奢,春花似錦熬甫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至豺谈,卻和暖如春郑象,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茬末。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工厂榛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丽惭。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓击奶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親责掏。 傳聞我的和親對象是個殘疾皇子柜砾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

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