原文出處:http://www.ccbu.cc/index.php/android/android-jni-dev-env.html
工欲善其事,必先利其器
。
1. 開發環境準備
搭建開發環境是我們進行開發前首先要完成的任務,進行Android jni開發,依賴的基本開發環境包括:
- Android sdk
- android ndk
- cmake
- android studio
Android studio的sdk manager已經包括了上面所說的sdk,ndk,cmake等工具的安裝,所以一般只用下載android studio,然後再使用sdk manager工具下載這些工具就可以了。
默認情況下,Android studio使用的編譯工具是cmake,但很多沿用的項目都是使用NDK的ndk-build工具來編譯的,所以android studio也支持ndk-build。
2. 使用android studio創建本地C++工程
1.新建工程, 在嚮導的 Choose your project 部分中,選擇Natvie C++
項目類型 。
2.在設置工程名,包名,保存路徑和語言,此處我們選擇Java語言。
3.在嚮導的 Customize C++ Support 部分中,可以選則C++ Toolchain
,一般情況下,選擇默認就可以,如果開發中需要用到C++11,或者c++14等一些較高級的C++標準的特性時,可以選擇對應的Toolchain
。
4.點擊finish,開始構建工程,工程構建完成以後,整個項目及其gradle配置文件如下:
默認情況下,Android studio使用cmake編譯鏈工具,通過gradle腳本進行配置,默認cmake配置如下:
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
cmake文件和c++源代碼都在src/main/cpp/
目錄下。
Android studio也支持ndk-buid
,根據實際需求,我們也可以配置爲ndk-build
,當然,這需要我們先寫好對應的Android.mk
和Appplication.mk(可選)
配置文件,然後通過修改gradle
配置中的externalNativeBuild
配置項來進行更改。配置爲nkd-build編譯工具,則其配置文件如下:
externalNativeBuild {
ndkBuild {
path file('src/main/cpp/Android.mk')
}
}
3. 使用現有android studio工程鏈接C++工程
當一個普通的不帶C++本地庫支持的項目需要引入一個現有的c++本地庫時,可以使用android studio的Link C++ Psroject with Gradle
功能來導入一個本地C++庫,導入的庫需要提供可用的cmake配置文件或Android.mk配置文件,導入工作是通過加載這些本地庫編譯配置文件來完成的。
4.在android studio配置javah工具
在Settings->Tools->External Tools下創建NDK group,在NDK group下創建javah工具。
詳細的配置參數如下:
配置項 | 參數 |
---|---|
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即可生成對應的c++本地函數定義的頭文件。
截圖中例子中,TestJni.java定義了本地函數add
package com.android.jnitest;
public class TestJni {
public native int add(int a, int b);
}
使用javah生成的對應本地c++頭文件com_android_jnitest_TestJni.h
內容如下:
#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 可選參數配置
可以在模塊級 build.gradle
文件的 defaultConfig
塊中配置另一個 externalNativeBuild
塊,爲 CMake 或 ndk-build 指定可選參數和標記 。
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
默認情況下,Gradle 會針對 NDK 支持的應用二進制接口 (ABI) 將您的原生庫編譯到單獨的 .so 文件中,並將這些文件全部打包到您的 APK 中。如果您希望 Gradle 僅編譯和打包原生庫的部分 ABI 配置,您可以在模塊級文件 build.gradle
中使用 ndk.abiFilters
標記指定這些配置,如下所示:
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 級別的後續發佈而逐漸添加的原生標頭和共享庫文件。 請執行以下兩個基本步驟的操作,以便讓您的應用使用 NDK 提供的庫:
-
在您的代碼中添加與您想使用的庫關聯的標頭。
-
通知編譯系統您的原生模塊需要在加載時鏈接庫。
- 如果您使用的是 ndk-build:將原生庫添加到您 Android.mk 文件中的 LOCAL_LDLIBS 變量中。例如,要鏈接 /system/lib/libfoo.so,請添加以下這行代碼:
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()
命令來關聯庫:target_link_libraries( # Specifies the target library. native-lib # Links the log library to the target library. ${log-lib} )
常用的android原生庫包括以下一些:
API級別 | 庫名 | 鏈接代碼 | 說明 |
---|---|---|---|
3 | C 庫 | 系統自動添加,無需配置 | |
3 | 動態鏈接器庫 | LOCAL_LDLIBS := -ldl |
動態鏈接器的 dlopen(3) 和 dlsym(3) 功能 |
3 | Android日誌庫 | LOCAL_LDLIBS := -llog |
原生代碼向 logcat 發送日誌消息 |
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 原生應用api | LOCAL_LDLIBS += -landroid |
使用原生代碼編寫整個 Android 應用 |
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 靜態庫爲 libc++_static.a |
C++17 支持。 |
system | /system/lib/libstdc++.so |
new 和 delete 。(在 r18 中已棄用。) |
none | 無頭文件,有限 C++。 |
libc++
libc++ 同時提供靜態庫和共享庫 。LLVM 的 libc++ 是 C++ 標準庫,自 Lollipop 以來 Android 操作系統便一直使用該庫,並且從 NDK r18 開始成爲 NDK 中唯一可用的 STL。libc++ 的共享庫爲 libc++_shared.so,靜態庫爲 libc++_static.a。
system
系統運行時指的是 /system/lib/libstdc++.so
。請勿將該庫與 GNU 的全功能 libstdc++ 混淆。在 Android 系統中,libstdc++ 只是 new
和 delete
。對於全功能 C++ 標準庫,請使用 libc++。
none
不包括STL。在這種情況下,沒有關聯或授權要求。不提供 C++ 標準頭文件。
7.2 配置C++ 運行時
如果您要使用 CMake,則可使用模塊級 build.gradle
文件中的 ANDROID_STL
變量,指定表表格中的一個運行時 。如果您要使用 ndk-build,則可使用 Application.mk 文件中的 APP_STL 變量指定表 1 中的一個運行時。
APP_STL := c++_shared
只能爲應用選擇一個運行時,並且只能在 Application.mk 中進行選擇。
7.3 共享運行時
如果應用包括多個共享庫,則應使用 libc++_shared.so
。
在 Android 系統中,NDK 使用的 libc++ 不是操作系統的一部分。這使得 NDK 用戶能夠獲得最新的 libc++ 功能和問題修復程序,即使應用以舊版 Android 爲目標。需要權衡的是,如果使用 libc++_shared.so
,則必須將其納入 APK 中。如果使用 Gradle 編譯應用,則此步驟會自動完成。
7.4 C++ 異常
C++ 異常受 libc++ 支持,但其在 ndk-build 中默認爲停用狀態。這是因爲之前 NDK 並不支持 C++ 異常。CMake 和獨立工具鏈默認啓用 C++ 異常。
要在 ndk-build 中針對整個應用啓用異常,請將下面這一行代碼添加至 Application.mk
文件:
APP_CPPFLAGS := -fexceptions
要針對單一 ndk-build 模塊啓用異常,請將下面這一行代碼添加至相應模塊的Android.mk
中:
LOCAL_CPP_FEATURES := exceptions
或者,您可以使用:
LOCAL_CPPFLAGS := -fexceptions
7.4 RTTI
與異常一樣,RTTI 也受 libc++ 支持,但在 ndk-build 中默認爲停用狀態。CMake 和獨立工具鏈默認啓用 RTTI。
要在 ndk-build 中針對整個應用啓用 RTTI,請將下面這一行代碼添加至 Application.mk
文件:
APP_CPPFLAGS := -frtti
要針對單一 ndk-build 模塊啓用 RTTI,請將下面這行代碼添加至相應模塊的 Android.mk
中:
LOCAL_CPP_FEATURES := rtti
或者,您可以使用:
LOCAL_CPPFLAGS := -frtti
參考: