原文出處:http://www.ccbu.cc/index.php/android/android-jni-dev-env.html
工欲善其事死相,必先利其器
。
1. 開發(fā)環(huán)境準(zhǔn)備
搭建開發(fā)環(huán)境是我們進(jìn)行開發(fā)前首先要完成的任務(wù)榆俺,進(jìn)行Android jni開發(fā),依賴的基本開發(fā)環(huán)境包括:
- Android sdk
- android ndk
- cmake
- android studio
Android studio的sdk manager已經(jīng)包括了上面所說的sdk,ndk,cmake等工具的安裝抵乓,所以一般只用下載android studio店归,然后再使用sdk manager工具下載這些工具就可以了。
默認(rèn)情況下醒叁,Android studio使用的編譯工具是cmake司浪,但很多沿用的項目都是使用NDK的ndk-build工具來編譯的,所以android studio也支持ndk-build把沼。
2. 使用android studio創(chuàng)建本地C++工程
1.新建工程啊易, 在向?qū)У?Choose your project 部分中,選擇Natvie C++
項目類型 饮睬。
2.在設(shè)置工程名租谈,包名,保存路徑和語言续捂,此處我們選擇Java語言垦垂。
3.在向?qū)У?Customize C++ Support 部分中,可以選則C++ Toolchain
牙瓢,一般情況下劫拗,選擇默認(rèn)就可以,如果開發(fā)中需要用到C++11,或者c++14等一些較高級的C++標(biāo)準(zhǔn)的特性時矾克,可以選擇對應(yīng)的Toolchain
页慷。
4.點擊finish,開始構(gòu)建工程,工程構(gòu)建完成以后酒繁,整個項目及其gradle配置文件如下:
默認(rèn)情況下滓彰,Android studio使用cmake編譯鏈工具,通過gradle腳本進(jìn)行配置州袒,默認(rèn)cmake配置如下:
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
cmake文件和c++源代碼都在src/main/cpp/
目錄下揭绑。
Android studio也支持ndk-buid
,根據(jù)實際需求郎哭,我們也可以配置為ndk-build
他匪,當(dāng)然,這需要我們先寫好對應(yīng)的Android.mk
和Appplication.mk(可選)
配置文件夸研,然后通過修改gradle
配置中的externalNativeBuild
配置項來進(jìn)行更改邦蜜。配置為nkd-build編譯工具,則其配置文件如下:
externalNativeBuild {
ndkBuild {
path file('src/main/cpp/Android.mk')
}
}
3. 使用現(xiàn)有android studio工程鏈接C++工程
當(dāng)一個普通的不帶C++本地庫支持的項目需要引入一個現(xiàn)有的c++本地庫時亥至,可以使用android studio的Link C++ Psroject with Gradle
功能來導(dǎo)入一個本地C++庫悼沈,導(dǎo)入的庫需要提供可用的cmake配置文件或Android.mk配置文件,導(dǎo)入工作是通過加載這些本地庫編譯配置文件來完成的姐扮。
4.在android studio配置javah工具
在Settings->Tools->External Tools下創(chuàng)建NDK group,在NDK group下創(chuàng)建javah工具絮供。
詳細(xì)的配置參數(shù)如下:
配置項 | 參數(shù) |
---|---|
Programe | $JDKPath$\bin\javah.exe |
Arguments | -classpath $ModuleFileDir$\src\main\java -jni -d $ModuleFileDir$\src\main\cpp $FileClass$ |
Working directory | $FileDir$ |
使用時,只需要在定義了native方法的java類上右鍵選擇NDK->javah即可生成對應(yīng)的c++本地函數(shù)定義的頭文件溶握。
截圖中例子中杯缺,TestJni.java定義了本地函數(shù)add
package com.android.jnitest;
public class TestJni {
public native int add(int a, int b);
}
使用javah生成的對應(yīng)本地c++頭文件com_android_jnitest_TestJni.h
內(nèi)容如下:
#include <jni.h>
/* Header for class com_android_jnitest_TestJni */
#ifndef _Included_com_android_jnitest_TestJni
#define _Included_com_android_jnitest_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_jnitest_TestJni
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_android_jnitest_TestJni_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
5. 工程配置
5.1 可選參數(shù)配置
可以在模塊級 build.gradle
文件的 defaultConfig
塊中配置另一個 externalNativeBuild
塊,為 CMake 或 ndk-build 指定可選參數(shù)和標(biāo)記 睡榆。
android {
...
defaultConfig {
...
// This block is different from the one you use to link Gradle
// to your CMake or ndk-build script.
externalNativeBuild {
// For ndk-build, instead use the ndkBuild block.
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
// Sets a flag to enable format macro constants for the C compiler.
cFlags "-D__STDC_FORMAT_MACROS"
// Sets optional flags for the C++ compiler.
cppFlags "-fexceptions", "-frtti"
}
}
}
...
}
通過上面的配置萍肆,可用對cmake的編譯選項及C和C++編譯選項做一些配置。
5.2 指定 ABI
默認(rèn)情況下胀屿,Gradle 會針對 NDK 支持的應(yīng)用二進(jìn)制接口 (ABI) 將您的原生庫編譯到單獨的 .so 文件中塘揣,并將這些文件全部打包到您的 APK 中。如果您希望 Gradle 僅編譯和打包原生庫的部分 ABI 配置宿崭,您可以在模塊級文件 build.gradle
中使用 ndk.abiFilters
標(biāo)記指定這些配置亲铡,如下所示:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {...}
// or ndkBuild {...}
}
// Similar to other properties in the defaultConfig block,
// you can configure the ndk block for each product flavor
// in your build configuration.
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a'
}
}
buildTypes {...}
externalNativeBuild {...}
}
6. 原生庫支持
Android NDK 會提供一組隨著新的 Android API 級別的后續(xù)發(fā)布而逐漸添加的原生標(biāo)頭和共享庫文件。 請執(zhí)行以下兩個基本步驟的操作葡兑,以便讓您的應(yīng)用使用 NDK 提供的庫:
在您的代碼中添加與您想使用的庫關(guān)聯(lián)的標(biāo)頭奖蔓。
-
通知編譯系統(tǒng)您的原生模塊需要在加載時鏈接庫。
- 如果您使用的是 ndk-build:將原生庫添加到您 Android.mk 文件中的 LOCAL_LDLIBS 變量中讹堤。例如吆鹤,要鏈接 /system/lib/libfoo.so,請?zhí)砑右韵逻@行代碼:
LOCAL_LDLIBS := -lfoo
要列出多個庫洲守,請使用空格作為分隔符疑务。
-
如果您使用的是 CMake沾凄,向 CMake 編譯腳本添加 find_library() 命令以找到 NDK 庫并將其路徑存儲為一個變量。
find_library( # Defines the name of the path variable that stores the # location of the NDK library. log-lib # Specifies the name of the NDK library that # CMake needs to locate. log )
然后再 CMake 腳本中的
target_link_libraries()
命令來關(guān)聯(lián)庫:target_link_libraries( # Specifies the target library. native-lib # Links the log library to the target library. ${log-lib} )
常用的android原生庫包括以下一些:
API級別 | 庫名 | 鏈接代碼 | 說明 |
---|---|---|---|
3 | C 庫 | 系統(tǒng)自動添加知允,無需配置 | |
3 | 動態(tài)鏈接器庫 | LOCAL_LDLIBS := -ldl |
動態(tài)鏈接器的 dlopen(3) 和 dlsym(3) 功能 |
3 | Android日志庫 | LOCAL_LDLIBS := -llog |
原生代碼向 logcat 發(fā)送日志消息 |
3 | ZLib 壓縮庫 | LOCAL_LDLIBS := -lz |
|
4 | OpenGL ES 1.x | LOCAL_LDLIBS := -lGLESv1_CM |
|
5 | OpenGL ES 2.0 | LOCAL_LDLIBS := -lGLESv2 |
|
8 | jnigraphics | LOCAL_LDLIBS += -ljnigraphics |
|
9 | EGL | LOCAL_LDLIBS += -lEGL |
分配和管理 OpenGLES 表面的原生平臺接口 |
9 | OpenSL ES | LOCAL_LDLIBS += -lOpenSLES |
原生音頻處理庫 |
9 | Android 原生應(yīng)用api | LOCAL_LDLIBS += -landroid |
使用原生代碼編寫整個 Android 應(yīng)用 |
14 | OpenMAX AL | LOCAL_LDLIBS += -lOpenMAXAL |
原生多媒體處理庫 |
14 | OpenSL ES | LOCAL_LDLIBS += -lOpenSLES |
增加了 PCM 支持 |
18 | OpenGL ES 3.0 | LOCAL_LDLIBS := -lGLESv3 |
|
21 | OpenGL ES 3.1 | LOCAL_LDLIBS := -lGLESv3 |
|
24 | OpenGL ES 3.2 | LOCAL_LDLIBS := -lGLESv3 |
7. C++ 庫支持
7.1 C++ 運行時庫
NDK 支持多種 C++ 運行時庫撒蟀。
名稱 | 庫文件 | 功能 |
---|---|---|
libc++ | 共享庫為 libc++_shared.so 靜態(tài)庫為 libc++_static.a
|
C++17 支持。 |
system | /system/lib/libstdc++.so |
new 和 delete 温鸽。(在 r18 中已棄用保屯。) |
none | 無頭文件,有限 C++嗤朴。 |
libc++
libc++ 同時提供靜態(tài)庫和共享庫 配椭。LLVM 的 libc++ 是 C++ 標(biāo)準(zhǔn)庫虫溜,自 Lollipop 以來 Android 操作系統(tǒng)便一直使用該庫雹姊,并且從 NDK r18 開始成為 NDK 中唯一可用的 STL。libc++ 的共享庫為 libc++_shared.so衡楞,靜態(tài)庫為 libc++_static.a吱雏。
system
系統(tǒng)運行時指的是 /system/lib/libstdc++.so
。請勿將該庫與 GNU 的全功能 libstdc++ 混淆瘾境。在 Android 系統(tǒng)中歧杏,libstdc++ 只是 new
和 delete
。對于全功能 C++ 標(biāo)準(zhǔn)庫迷守,請使用 libc++犬绒。
none
不包括STL。在這種情況下兑凿,沒有關(guān)聯(lián)或授權(quán)要求凯力。不提供 C++ 標(biāo)準(zhǔn)頭文件。
7.2 配置C++ 運行時
如果您要使用 CMake礼华,則可使用模塊級 build.gradle
文件中的 ANDROID_STL
變量咐鹤,指定表表格中的一個運行時 。如果您要使用 ndk-build圣絮,則可使用 Application.mk 文件中的 APP_STL 變量指定表 1 中的一個運行時祈惶。
APP_STL := c++_shared
只能為應(yīng)用選擇一個運行時,并且只能在 Application.mk 中進(jìn)行選擇扮匠。
7.3 共享運行時
如果應(yīng)用包括多個共享庫捧请,則應(yīng)使用 libc++_shared.so
。
在 Android 系統(tǒng)中棒搜,NDK 使用的 libc++ 不是操作系統(tǒng)的一部分疹蛉。這使得 NDK 用戶能夠獲得最新的 libc++ 功能和問題修復(fù)程序,即使應(yīng)用以舊版 Android 為目標(biāo)帮非。需要權(quán)衡的是氧吐,如果使用 libc++_shared.so
讹蘑,則必須將其納入 APK 中。如果使用 Gradle 編譯應(yīng)用筑舅,則此步驟會自動完成座慰。
7.4 C++ 異常
C++ 異常受 libc++ 支持,但其在 ndk-build 中默認(rèn)為停用狀態(tài)翠拣。這是因為之前 NDK 并不支持 C++ 異常版仔。CMake 和獨立工具鏈默認(rèn)啟用 C++ 異常。
要在 ndk-build 中針對整個應(yīng)用啟用異常误墓,請將下面這一行代碼添加至 Application.mk
文件:
APP_CPPFLAGS := -fexceptions
要針對單一 ndk-build 模塊啟用異常蛮粮,請將下面這一行代碼添加至相應(yīng)模塊的Android.mk
中:
LOCAL_CPP_FEATURES := exceptions
或者,您可以使用:
LOCAL_CPPFLAGS := -fexceptions
7.4 RTTI
與異常一樣谜慌,RTTI 也受 libc++ 支持然想,但在 ndk-build 中默認(rèn)為停用狀態(tài)。CMake 和獨立工具鏈默認(rèn)啟用 RTTI欣范。
要在 ndk-build 中針對整個應(yīng)用啟用 RTTI变泄,請將下面這一行代碼添加至 Application.mk
文件:
APP_CPPFLAGS := -frtti
要針對單一 ndk-build 模塊啟用 RTTI,請將下面這行代碼添加至相應(yīng)模塊的 Android.mk
中:
LOCAL_CPP_FEATURES := rtti
或者恼琼,您可以使用:
LOCAL_CPPFLAGS := -frtti
參考: