前言
本文基于自己所學(xué)到的NDk的知識和一些在網(wǎng)上查到的資料
因?yàn)閷W(xué)習(xí)NDK的時(shí)間不是很長秉剑,學(xué)到的內(nèi)容難免有所錯(cuò)漏,希望有問題的可以明確的指出來错英,我會積極采納
本文會竭盡可能的將我現(xiàn)在所學(xué)到的關(guān)于NDK的知識清晰的表達(dá)出來,本文主要以NDK的基本概念荆烈,NDK的組成部分淌山,NDK的注意事項(xiàng)三個(gè)部分組成
實(shí)踐部分因?yàn)楸酒恼乱呀?jīng)6000多字了裸燎,為了不讓文章過長,將會在后面的博客中
一. NDK的基本概念
在學(xué)習(xí)NDK之前泼疑,我首先得知道什么是NDK德绿,NDK可以干些什么,使用NDK的好處有哪些退渗,知道了這些移稳,我們才可以更好的學(xué)習(xí)NDK
1. 什么是NDK
NDK即Native Development Kit
,是Android中的一個(gè)開發(fā)工具包,為我們提供native開發(fā)的環(huán)境
NDK是我們實(shí)現(xiàn)Java與Native進(jìn)行交互的一個(gè)手段
2. NDK可以干什么
可以快速開發(fā)C
、 C++
的動態(tài)庫会油,并自動將so
和應(yīng)用一起打包成 APK
即可通過 NDK
使 Java
與native代碼(如 C个粱、C++)交互
NDK在現(xiàn)在很多熱門的技術(shù)中都有使用,如:Android 音視頻開發(fā)翻翩,熱更新都许,OpenCV,等等
3. 我們?yōu)槭裁匆褂肗DK
允許程序開發(fā)人員直接使用 C/C++ 源代碼嫂冻,極大的提高了 Android 應(yīng)用程序開發(fā)的靈活性
跨平臺應(yīng)用移植胶征、使用第三方庫。如:許多第三方庫只有 C/C++ 語言的版本桨仿,而 Android 應(yīng)用程序需要使用現(xiàn)有的第三方庫睛低,如 FFmpeg、OpenCV等服傍,則必須使用NDK
采用C++代碼來處理性能要求高的操作钱雷,提高了Android APP的性能
安全性高
4. NDK與SDK的關(guān)系
在Android開發(fā)中,最常用的是SDK吹零,那么SDK與NDK的關(guān)系是什么呢罩抗?
在SDK中,我們使用Java來進(jìn)行開發(fā),而在NDK中,我們使用C++來進(jìn)行開發(fā)
SDK支持了Android開發(fā)中的大部分操作搅荞,如UI展示发框,用戶與手機(jī)的交互等,主要是支持了Android APP開發(fā)的基礎(chǔ)功能
NDK支持了一些復(fù)雜的伸辟,比較高級的操作麻惶,如音視頻的解析,大量數(shù)據(jù)的運(yùn)算信夫,提高Android游戲的運(yùn)行速度等窃蹋,主要是Android APP的一些高級功能
所以NDK與SDK是并列關(guān)系卡啰,NDK是SDK的有效補(bǔ)充
二. NDK的組成部分
現(xiàn)在我們知道了NDK是什么,NDK的作業(yè)警没,優(yōu)點(diǎn)了匈辱,那我們該開始正式學(xué)習(xí)NDK了,但是工欲善其事杀迹,必先利其器
亡脸,在使用NDK這個(gè)工具以前,我們必須先好好地了解NDK
所以本部分將分析NDK中的一些組成及他們的作用
本部分將講述NDK中的JNI
树酪,二進(jìn)制文件(.so 和 .a), ABI 浅碾,本機(jī)編譯工具,交叉編譯工具等等
1. JNI
-
定義
JNI 即
Java Native Interface
,即Javanative接口JNI是一種編程框架续语,使得 Java 虛擬機(jī)中的 Java 程序可以調(diào)用本地應(yīng)用 / 或庫垂谢,也可以被其他程序調(diào)用。 本地程序一般是用其它語言(C疮茄、C++ 或匯編語言等)編寫的滥朱,并且被編譯為基于本機(jī)硬件和操作系統(tǒng)的程序
上文中,NDK也是支持Java代碼與Native代碼的交互娃豹,那他們之間有什么區(qū)別呢焚虱?
實(shí)際上,JNI是一個(gè)編程框架懂版,是一個(gè)抽象的東西鹃栽,NDK是一個(gè)工具包,是一個(gè)
所以:NDK是實(shí)現(xiàn)JNI的一個(gè)手段
作用 支持Java代碼與native代碼進(jìn)行交互躯畴,即Java代碼調(diào)用native代碼 或者 native代碼調(diào)用Java代碼 native代碼主要指
C
和C++
-
使用JNI的原因
有些事情 Java 無法處理時(shí)民鼓,JNI 允許程序員用其他編程語言來解決,例如蓬抄,Java 標(biāo)準(zhǔn)庫不支持的平臺相關(guān)功能或者程序庫丰嘉。也用于改造已存在的用其它語言寫的程序,供 Java 程序調(diào)用
-
使用JNI的步驟
使用
Native
關(guān)鍵字定義Java
方法(即需要調(diào)用的native方法)使用
javac
編譯上述Java
源文件 (即.java文件
)最終得到.class
文件通過
javah
命令編譯.class
文件,最終導(dǎo)出JNI
的頭文件(.h
文件)使用
Java
需要交互的native代碼嚷缭,以及實(shí)現(xiàn)在Java
中聲明的Native
方法編譯
.so
庫文件通過
Java
命令執(zhí)行Java
程序饮亏,最終實(shí)現(xiàn)Java
調(diào)用native
代碼編譯生成的.so文件
實(shí)際上2345步驟的目的就是生成.so 文件
所以上面的步驟可以歸納為三步:
聲明Native方法
實(shí)現(xiàn)Native方法,經(jīng)過一系列操作阅爽,最終生成.so 文件
-
加載.so文件路幸,調(diào)用Native方法
這里只簡單介紹JNI的步驟,具體實(shí)現(xiàn)的例子在后面的博客中
2 .so 和 .a 文件
上面我們已經(jīng)說到付翁,JNI 支持了Java
代碼和native
代碼的互相調(diào)用
但是JNI是直接調(diào)用Java代碼和native代碼嗎简肴?
實(shí)際上,JNI是調(diào)用
java
代碼和native
代碼編譯后的.so
和.a
文件來實(shí)現(xiàn)了Java代碼和native代碼的交互
那么.so
和.a
文件是什么呢百侧?下面我列出了.so和.a文件的一些定義
-
動態(tài)鏈接庫 (.so 后綴):
運(yùn)行時(shí)才動態(tài)加載這個(gè)庫;
動態(tài)鏈接庫砰识,也叫共享庫能扒,因?yàn)樵?NDK 中用
shared
來表示是動態(tài)庫現(xiàn)在熱門的插件化,熱更新以及縮小APK大小等技術(shù)都使用了
.so
文件 -
靜態(tài)鏈接庫 (.a 后綴)
在編譯的時(shí)候, 就把靜態(tài)庫打包進(jìn) APK 中
缺點(diǎn) : 使用靜態(tài)庫編譯, 編譯的時(shí)間比較長辫狼,同時(shí)也使得APK比較大
優(yōu)點(diǎn) : 只導(dǎo)出一個(gè)庫, 可以隱藏自己調(diào)用的庫;
.so
和.a
本質(zhì)上都是二進(jìn)制文件初斑,下文我將用二進(jìn)制文件統(tǒng)稱這兩個(gè)文件每個(gè)CPU系統(tǒng)只能使用相對應(yīng)的二進(jìn)制文件,即他們不像
jar
包一樣予借,所有的CPU系統(tǒng)都可以使用一個(gè)jar包越平,.so 和 .a 每個(gè)系統(tǒng)必須使用自己的,不能使用別的灵迫,如armeabi
的.so
文件秦叛,不能被應(yīng)用到x86
中
3. CPU架構(gòu)
Android 平臺,其支持的設(shè)備型號繁多瀑粥,單單就設(shè)備的核心 CPU 而言挣跋,都有三大類:ARM、x86 和 MIPS
**ARM主要應(yīng)用于手機(jī)中狞换,x86主要應(yīng)用于PC中
Android中使用x86
主要是因?yàn)椋?strong>因?yàn)镻C是x86架構(gòu)避咆,所以PC上的手機(jī)模擬器需要x86的二進(jìn)制文件
而在NDK r17中,有了大的變化:
在NDK r17 以后修噪,NDK 不在支持32位和64位 MIPS
和ARM v5(armeabi)
所以現(xiàn)在NDK只支持ARM
和x86
查库,而ARM
和x86
又各自分為兩種:
簡單的來說:ARM 和 x86 各分為 32位和64位兩種,所以現(xiàn)在NDK一共支持4種CPU架構(gòu)
即:ARM 32位 黄琼,ARM 64位 樊销, x86 32位 ,x86 64位
4. ABI
ABI : Application Binary Interface
我們上面說了脏款,每個(gè)系統(tǒng)只能使用相對應(yīng)的二進(jìn)制文件围苫,
不同的 Android 設(shè)備使用不同的 CPU,而不同的 CPU 支持不同的指令集撤师。CPU 與指令集的每種組合都有專屬的應(yīng)用二進(jìn)制接口 (ABI)
簡而言之:而ABI定義了二進(jìn)制文件是怎么運(yùn)行在對應(yīng)的CPU中的
上文中我們大致了解了Android中常用的CPU架構(gòu)剂府,而且我們知道,ABI 定義了二進(jìn)制文件時(shí)怎么在CPU中運(yùn)行的剃盾,那么我們可以知道腺占,每一個(gè)CPU架構(gòu)必定有一個(gè)相對應(yīng)的ABI
上面我們已經(jīng)知道了有四種,那么ABI也有四種痒谴,他們分別是:armeabi-v7a衰伯,armeabi-v8a,x86闰歪,x86_64
ABI | 對應(yīng)的CPU架構(gòu) | 應(yīng)用 |
---|---|---|
armeabi-v7a | ARM 32位 | 手機(jī) |
armeabi-v8a | ARM 64位 | 手機(jī) |
X86 | X86 32位 | PC |
X86_64 | X86 64位 | PC |
CPU架構(gòu)中64位的CPU架構(gòu)兼容32位的ABI和64位的ABI嚎研,32位的CPU架構(gòu)只支持32位的ABI
armeabi-v7a設(shè)備只兼容armeabi-v7a
armeabi-v8a設(shè)備兼容armeabi-v7a蓖墅,armeabi-v7a
X86設(shè)備只兼容X86_64
X86_64兼容X86库倘,X86_64
NDK編譯實(shí)際上默認(rèn)編譯出所有系統(tǒng)的文件
但是有時(shí)我們只需要使用指定的系統(tǒng)临扮,我們就可以指定編譯什么系統(tǒng),減少二進(jìn)制文件教翩,避免我們不會使用到的二進(jìn)制文件被打包到apk中杆勇,如我們在PC上使用模擬器來執(zhí)行APP時(shí),我們需要x86饱亿,但是我們APP最終是要在手機(jī)上運(yùn)行的蚜退,這樣我們只需要ARM的就行了,我們就可以在最終打包APK時(shí)彪笼,去掉x86的
我們可以使用下面的代碼來指定我們要編譯什么CPU架構(gòu)的二進(jìn)制文件
//在project的build.gradle中
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
5. 編譯工具
5.1 本機(jī)編譯工具
我們已經(jīng)知道钻注,每個(gè)系統(tǒng)只能使用自己系統(tǒng)的二進(jìn)制文件,本機(jī)編譯工具正是編譯出本機(jī)系統(tǒng)可以使用的二進(jìn)制文件
在Android中可以使用的本機(jī)編譯工具有兩種:ndk-build
和 cmake
-
cmake
我們已經(jīng)知道配猫,每個(gè)系統(tǒng)只能使用自己的二進(jìn)制文件
那么我們在開發(fā)中幅恋,就需要編譯出對應(yīng)的二進(jìn)制文件,而且如果我們的軟件想要跨平臺泵肄,那么我們就得保證在每一個(gè)平臺上都可以編譯
即:如果我們的軟件想在Mac OS 和 Windows上運(yùn)行捆交,那么我們的軟件在Mac OS上運(yùn)行時(shí),要生成可以在Mac OS上運(yùn)行的二進(jìn)制文件腐巢,在Windows上運(yùn)行品追,要生成Windows上可以運(yùn)行的二進(jìn)制文件
這樣的話,那我們得為每一個(gè)平臺都要編寫一次
MakeFile
文件冯丙,這將是一個(gè)很無趣肉瓦,很令人抓狂的操作
而CMake就是針對上述問題的一個(gè)工具
它允許開發(fā)者編寫一種與平臺無關(guān)的CMakeList.txt文件來定制整個(gè)編譯流程,然后再根據(jù)目標(biāo)用戶的平臺逐步生成所需的本地化Makefile和工程文件银还,如Unix的Makefile或Windows的Visual Studio工程
從而達(dá)到“只寫一次风宁,到處運(yùn)行”
即:我們只要寫一個(gè) cmakeLists.txt
文件,那么就可以在所有平臺中編譯出對應(yīng)的二進(jìn)制文件
這樣的話蛹疯,當(dāng)運(yùn)行在Windows上時(shí)戒财,自動生成Windows的,在Mac OS行運(yùn)行捺弦,自動生成MacOS的饮寞,但是,我們只需要寫一個(gè)cmakeList.txt
文件即可A泻稹S谋馈!
使用 CMake 生成 Makefile 并編譯的流程如下:
- 編寫 CMake 配置文件 CMakeLists.txt 寞钥。
- 執(zhí)行命令
cmake PATH
或者ccmake PATH
生成 Makefile(ccmake
和cmake
的區(qū)別在于前者提供了一個(gè)交互式的界面)慌申。其中,PATH
是 CMakeLists.txt 所在的目錄。 - 使用
make
命令進(jìn)行編譯蹄溉。
當(dāng)然咨油,在Android開發(fā)過程中,我們不需要執(zhí)行上面的流程柒爵,因?yàn)樵?Android studio 當(dāng)中已經(jīng)為我們集成了役电,我們只需要編寫cmakeLists.txt
文件,剩下的就交給 Android studio 就行了
在Android開發(fā)過程中棉胀,使用cmake只需要兩個(gè)文件法瑟,xxx.cpp
和CMakeLists.txt
- cmakeLists.txt 就如上面所述,定義整個(gè)的編譯流程
- xxx.cpp 則是要被編譯的 C++ 文件
具體的實(shí)現(xiàn)方式我會在下面列出
- ndk-build
ndk-build是一個(gè)和cmake功能差不多的工具唁奢,他們都減少了我們定制編譯流程的操作
但是ndk-build是以前常使用的工具霎挟,我們現(xiàn)在常用cmake,ndk-build的操作要比cmake復(fù)雜一些
ndk-build
本質(zhì)上是一個(gè)腳本,它的位置就在NDK目錄的最上層麻掸,即在< NDK >/ndk-build
路徑下
因?yàn)槭且粋€(gè)腳本氓扛,所以下面的命令等同于ndk-build
# $GNUMAKE 指 GNU Make 3.81 或更高版本
# <ndk> 則指向 NDK 安裝目錄
$GNUMAKE -f <ndk>/build/core/build-local.mk <parameters>
#等同于
ndk-build
使用ndk-build我們需要兩個(gè)文件: Android.mk 和 Application.mk
-
Android.mk
Google的官方文檔對Android.mk的定義如下
Android.mk
文件位于項(xiàng)目jni/
目錄的子目錄中,用于向構(gòu)建系統(tǒng)描述源文件和共享庫论笔。它實(shí)際上是構(gòu)建系統(tǒng)解析一次或多次的微小 GNU makefile 片段采郎。Android.mk
文件用于定義Application.mk
、構(gòu)建系統(tǒng)和環(huán)境變量所未定義的項(xiàng)目級設(shè)置狂魔。它還可替換特定模塊的項(xiàng)目級設(shè)置蒜埋。Android.mk
的語法支持將源文件分組為“模塊”。模塊是靜態(tài)庫最楷、共享庫或獨(dú)立的可執(zhí)行文件整份。您可在每個(gè)Android.mk
文件中定義一個(gè)或多個(gè)模塊,也可在多個(gè)模塊中使用同一個(gè)源文件籽孙。構(gòu)建系統(tǒng)只將共享庫放入您的應(yīng)用軟件包烈评。此外,靜態(tài)庫可生成共享庫犯建。除了封裝庫之外讲冠,構(gòu)建系統(tǒng)還可為您處理各種其他事項(xiàng)。例如适瓦,您無需在
Android.mk
文件中列出頭文件或生成的文件之間的顯式依賴關(guān)系竿开。NDK 構(gòu)建系統(tǒng)會自動計(jì)算這些關(guān)系。因此玻熙,您應(yīng)該能夠享受到未來 NDK 版本中支持的新工具鏈/平臺功能帶來的益處否彩,而無需處理Android.mk
文件。此文件的語法與隨整個(gè) Android 開源項(xiàng)目分發(fā)的
Android.mk
文件中使用的語法非常接近嗦随。雖然使用這些語法的構(gòu)建系統(tǒng)實(shí)現(xiàn)并不相同列荔,但通過有意將語法設(shè)計(jì)得相似,可使應(yīng)用開發(fā)者更輕松地將源代碼重復(fù)用于外部庫。
這個(gè)的定義很長贴浙,簡單的來說筷转,Android.mk的作用如下:
- 描述了源文件的位置和名字
- 描述了生成什么文件,如共享庫悬而,靜態(tài)庫等
- 自動處理構(gòu)建中的一些事項(xiàng),如文件之間的依賴關(guān)系
- 語法設(shè)置得與Android開源項(xiàng)目相似锭汛,使得我們可以輕易的將源代碼重復(fù)使用
Android.mk的一些常用的變量如下:
# 這個(gè)是源文件的路徑笨奠,call my-dir表示了返回當(dāng)前Android.mk所在的目錄
LOCAL_PATH := $(call my-dir)
# 清除許多 LOCAL_XXX 變量
# 注意:不會清除 LOCAL_PATH
include $(CLEAR_VARS)
# LOCAL_MODULE 變量存儲要構(gòu)建的模塊的名稱
# 這里最終會生成叫 libhello-jni.so 的文件
LOCAL_MODULE := hello-jni
# 源文件名字
# 可以有多個(gè)源文件,使用空格隔開
LOCAL_SRC_FILES := hello-jni.c
# 指定編譯出什么二進(jìn)制文件
# 這里編譯出共享庫唤殴,即:.so文件
# 編譯出靜態(tài)庫可以使用: BUILD_STATIC_LIBRARY
include $(BUILD_SHARED_LIBRARY)
-
Application.mk
Application.mk
指定 ndk-build 的項(xiàng)目范圍設(shè)置般婆。默認(rèn)情況下,它位于應(yīng)用項(xiàng)目目錄中的jni/Application.mk
下簡單的來說朵逝,Application.mk的功能主要是:主要是描述了Android Native開發(fā)需要的模組(module)
一些常用的變量如下:
# 定義生成的二進(jìn)制文件要生成的CPU架構(gòu)
# 這里指定生成 arm64-v8a 可以用的二進(jìn)制文件
APP_ABI := arm64-v8a
# 定義可以使用該二進(jìn)制文件的Android版本
APP_PLATFORM := android-21
# 默認(rèn)情況下蔚袍,ndk-build 假定 Android.mk 文件位于項(xiàng)目根目錄的相對路徑 jni/Android.mk 中。
# 要從其他位置加載 Android.mk 文件配名,將 APP_BUILD_SCRIPT 設(shè)置為 Android.mk 文件的絕對路徑啤咽。
APP_BUILD_SCRIPT
?
-
cmake 和 ndk-build 都可以在Android開發(fā)中使用,但是現(xiàn)在默認(rèn)使用的是cmake渠脉,以前常用的是ndk-build
ndk-build
的實(shí)現(xiàn)過程比cmake
復(fù)雜的多,所以現(xiàn)在推薦使用cmake但是因?yàn)橐郧暗捻?xiàng)目常用
ndk-build
宇整,如果我們要參與以前 NDK項(xiàng)目 的開發(fā),那么ndk-build
也是需要了解的所以芋膘,如果要新建一個(gè)項(xiàng)目鳞青,那么推薦使用
cmake
,要參與以前老項(xiàng)目的開發(fā),ndk-build
也不可落下
5.2交叉編譯工具
與本機(jī)編譯對應(yīng)的为朋,有時(shí)我們需要編譯出其他系統(tǒng)的二進(jìn)制文件臂拓,如我們在PC上寫Android文件,那么我們PC中就需要編譯出Android中可以運(yùn)行的二進(jìn)制文件
交叉編譯工具习寸,又叫交叉編譯鏈( toolchain
)
交叉編譯鏈(編譯器胶惰、鏈接器等)來生成可以在其他系統(tǒng)中運(yùn)行的二進(jìn)制文件
在NDK中,交叉編譯工具主要有兩種:clang
和gcc
三. NDK中一些值得注意的事情
1. NDK版本變化的問題
1.1 NDK中編譯工具的變化
cmake 和 ndk-build 都可以在Android開發(fā)中使用霞溪,但是現(xiàn)在默認(rèn)使用的是cmake童番,以前常用的是ndk-build
ndk-build
的實(shí)現(xiàn)過程比cmake
復(fù)雜的多,所以現(xiàn)在推薦使用cmake
但是因?yàn)橐郧暗捻?xiàng)目常用 ndk-build
,如果我們要參與以前 NDK項(xiàng)目 的開發(fā)威鹿,那么 ndk-build
也是需要了解的
所以剃斧,如果要新建一個(gè)項(xiàng)目,那么推薦使用cmake
,要參與以前老項(xiàng)目的開發(fā)忽你,ndk-build
也不可落下
1.2 NDK中交叉編譯的工具的變化
在ndk r17c 以后默認(rèn)使用的變成了clang幼东,而不是gcc
庫文件和頭文件的變化
在r17c以前,頭文件,庫文件以及gcc的路徑如下:
# 庫文件路徑 android-ndk-r17c/platforms/android-21/arch-arm/usr/lib # 頭文件路徑 android-ndk-r17c/sysroot/usr/include #gcc的路徑 android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin
而在r17以后根蟹,如 r20 中脓杉,頭文件和庫文件統(tǒng)一放到了sysroot
中:
#頭文件
toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include
#庫文件
toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib
我們可以發(fā)現(xiàn) r20 把頭文件和庫文件弄到了一起
但是,r20 中也沒有把 r17 中的庫文件刪除简逮,即我們也可以在下面的路徑中找到頭文件
android-ndk-r17c/platforms/android-21/arch-arm/usr/lib
交叉編譯工具位置的變化
在 NDK r19以前的 ndk
內(nèi)置了一個(gè)可以自動生成交叉編譯工具(toolchain)
的.py
文件球散,放在
ndk路徑下面的build/tool/make_standalone_toolchain.py
要生成toolchain,使用下面的命令
$NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm --api 21 --install-dir /Users/fczhao/Desktop
后面的幾個(gè)都是必要的
--arch 指定了生成的toolchain要在哪個(gè)CPU框架中使用
--api 指定了生成的toolchain要在哪個(gè)Android API 中使用
--install-dir 生成的toolchain的路徑
如果使用的是NDK r19以前的
散庶,可以參考下面的這個(gè)文章
https://developer.android.com/ndk/guides/standalone_toolchain?hl=zh-cn
但是NDK r19
以后的NDK已經(jīng)內(nèi)置了這些文件蕉堰,如果運(yùn)行上面的命令,會出現(xiàn)這樣的日志
WARNING:__main__:make_standalone_toolchain.py is no longer necessary. The
#$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin 這個(gè)路徑已經(jīng)有了我們要生成的文件
$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin directory contains target-specific scripts that perform
the same task. For example, instead of:
$ python $NDK/build/tools/make_standalone_toolchain.py \
--arch arm --api 21 --install-dir toolchain
$ toolchain/bin/clang++ src.cpp
Instead use:
$ $NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang++ src.cpp
Installation directory already exists. Use --force.
然后讓我們?nèi)ド厦孑敵龅穆窂街锌纯幢辏涂梢钥吹絅DK的確已經(jīng)提供了很多的clang,這里我只截取了一部分
1.3 NDK支持的CPU架構(gòu)的變化
上文中我們在ABI已經(jīng)提到了CPU架構(gòu)相關(guān)的東西屋讶,但是為了增加印象,這里稍微重新提一下
Android 平臺核心 CPU有三大類:ARM须教、x86 和 MIPS
而在NDK r17中皿渗,有了大的變化:
在NDK r17 以后,NDK 不在支持32位和64位 MIPS
和ARM v5(armeabi)
所以現(xiàn)在NDK中只支持armeabi-v7a轻腺,armeabi-v8a乐疆,x86,x86_64四類
2. NDK 的系統(tǒng)是否適用的問題
值得注意的是:
在下載NDK時(shí),不像我們下載JDK一樣
當(dāng)下載同一版本的JDK時(shí)贬养,所有的電腦(不論是macOS诀拭,Windows還是Linus)下載的都是一樣
而當(dāng)我們下載相同版本的NDK時(shí),會發(fā)現(xiàn)Google提供了適合于不同系統(tǒng)的NDK煤蚌,如下圖
NDK截圖之所以提供針對每個(gè)系統(tǒng)的NDK的原因是是
因?yàn)镴ava良好的跨平臺性耕挨,所以所有的系統(tǒng)都可以使用同一個(gè)JDK
而NDK調(diào)用的是C++代碼,C++沒有這么好的跨平臺性尉桩,從而我們必須為每一個(gè)平臺配置適合于它的NDK
所以在我們看別人的NDK博客時(shí)筒占,一定要注意是否與自己使用的NDK是一個(gè)系統(tǒng)的,一個(gè)版本的蜘犁,否則一定會出現(xiàn)問題
3. NDK 中Java與native代碼的交互
Java 和 c/c++ 有兩種交互方式:
Java調(diào)用 native 代碼
native 代碼調(diào)用 Java 代碼
也就是說翰苫,交互是Java與C++之間的互相調(diào)用
但是在NDK中他們實(shí)際上的順序是:
Java調(diào)用C++代碼,然后C++調(diào)用Java代碼返回C++執(zhí)行后的數(shù)據(jù)这橙,如圖所示
從圖中可以看到奏窑,我們使用NDK的目的實(shí)際上是:
? Java調(diào)用C++,讓C++處理一些復(fù)雜的操作屈扎,然后把處理后的數(shù)據(jù)返回到Java中
? 而我們?yōu)槭裁床恢苯邮褂肑ava來進(jìn)行處理埃唯,而是繞了一圈,通過 Java -> C++ -> Java
的方式來實(shí)現(xiàn)鹰晨,其中的原因是:
? C++速度比Java快
? 舉個(gè)簡單的例子墨叛,在ACM或者LeetCode中止毕,C++代碼運(yùn)行的超時(shí)時(shí)間是 1s,而Java的是 2s
? 所以對于一些計(jì)算量很大的操作漠趁,如音視頻的渲染等我們采用C++可以有效的減少計(jì)算時(shí)間和內(nèi)存占用扁凛,減少手機(jī)發(fā)熱和耗電量等
現(xiàn)在我們已經(jīng)大致了解了一些NDK的東西了,后面的博客我將分別分析cmake闯传,ndk-build谨朝,clang,gcc的具體實(shí)現(xiàn)過程