Android Studio配置CMake開發NDK
阿新 • • 發佈:2019-01-26
1.在SDK Tools中勾選安裝CMake、LLDB、NDK
2.配置一些快捷方式
引數講解
javah 用於生成標頭檔案
Program:$JDKPath$/bin/javah
Parameters:-d ../jni -jni $FileClass$
Working directory:$SourcepathEntry$\..\java
ndk-build 用於構建so包
注意:MAC/Linux用ndk-build,沒有.cmd字尾
Program:D:\adt\sdk\ndk-bundle\ndk-build .cmd
Parameters:什麼都不用填
Working directory:$ModuleFileDir$\src\main
3.在工程的local.properties檔案中配置NDK的目錄
sdk.dir=C\:\\Users\\yuxue\\AppData\\Local\\Android\\sdk
ndk.dir=C\:\\Users\\yuxue\\AppData\\Local\\Android\\sdk\\ndk-bundle
也可以使用圖形介面,單擊模組選擇Open Moude Setting,選擇好NDK的路徑
4.編譯時如果檢查NDK過時了可以在gradle.properties檔案中增加“android.useDeprecatedNdk=true”使它可以使用過時的NDK
android.useDeprecatedNdk=true
5.建立CMakeLists.txt檔案並放在模組的的根目錄
# 設定構建本地庫所需的最小版本的cbuild。
cmake_minimum_required(VERSION 3.4.1)
# 建立並命名一個庫,將其設定為靜態
# 或者共享,並提供其原始碼的相對路徑。
# 您可以定義多個庫,而cbuild為您構建它們。
# Gradle自動將共享庫與你的APK打包。
add_library( hello-lib #設定庫的名稱。即SO檔案的名稱,生產的so檔案為“libhello-lib.so”,在載入的時候“System.loadLibrary("hello-lib");”
SHARED # 將庫設定為共享庫。
src/main/jni/hello.cpp # 提供一個原始檔的相對路徑
src/main/jni/helloJni.cpp # 提供同一個SO檔案中的另一個原始檔的相對路徑
)
#搜尋指定的預構建庫,並將該路徑儲存為一個變數。因為cbuild預設包含了搜尋路徑中的系統庫,所以您只需要指定您想要新增的公共NDK庫的名稱。cbuild在完成構建之前驗證這個庫是否存在。
find_library(log-lib # 設定path變數的名稱。
log # 指定NDK庫的名稱 你想讓CMake來定位。
)
#指定庫的庫應該連結到你的目標庫。您可以連結多個庫,比如在這個構建指令碼中定義的庫、預構建的第三方庫或系統庫。
target_link_libraries( hello-lib #指定目標庫中。與 add_library的庫名稱一定要相同
${log-lib} # 將目標庫連結到日誌庫包含在NDK。
)
#如果需要生產多個SO檔案的話,寫法如下
add_library( natave-lib #設定庫的名稱。另一個so檔案的名稱
SHARED # 將庫設定為共享庫。
src/main/jni/nataveJni.cpp # 提供一個原始檔的相對路徑
)
target_link_libraries( natave-lib #指定目標庫中。與 add_library的庫名稱一定要相同
${log-lib} # 將目標庫連結到日誌庫包含在NDK。
)
6.在模組的build.gradle檔案中新增
android {
compileSdkVersion 26
buildToolsVersion "26.0.0"
defaultConfig {
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
7.編寫Java中的Native方法
public native String getStr();
public native String gethelloJniStr();
#8.生成C的標頭檔案
生成後
9.編寫C函式
#include "stdio.h"
#include "jni.h"
#include "string"
extern "C"
JNIEXPORT jstring JNICALL Java_com_ljp_learnandroidadvanced_MainActivity_getStr
(JNIEnv *env,
jobject jobject1){
return env->NewStringUTF("hello world from cpp");
}
至此,這個專案就可以運行了
更多的學習
(1) .externalNativeBuild資料夾:用於存放cmake編譯好的檔案,包括支援的各種硬體等資訊,有點類似於build.gradle檔案明確Gradle如何編譯APP;
(2) cpp資料夾:存放C/C++程式碼檔案,native-lib.cpp檔案預設生成的;
(3) CMakeLists.txt:cmake指令碼配置檔案,cmake會根據該指令碼檔案中的指令去編譯相關的C/C++原始檔,並將編譯後產物生成共享庫或靜態塊,然後Gradle將其打包到APK中。
CMakeLists.txt檔案解析如下:
# 指定cmke版本
cmake_minimum_required(VERSION3.4.1)
# add_library()命令用於向CMake新增依賴原始檔或庫
# 指令需傳入三個引數(函式庫名稱、庫型別、依賴原始檔相對路徑)
add_library( # 生成函式庫的名稱,即libnative-lib.so或libnative-lib.a(lib和.so/.a預設預設)
native-lib
# 生成庫型別:動態庫為SHARED,靜態庫為STATIC
SHARED
# 依賴的c/cpp檔案(相對路徑)
src/main/cpp/native-lib.cpp )
# find_library()命令用於定位NDK中的庫
# 需傳入兩個引數(path變數、ndk庫名稱)
find_library( # 設定path變數的名稱,這裡為NDK中的日誌庫
log-lib
#指定cmake查詢庫的名稱
#即在ndk開發包中查詢liblog.so函式庫,將其路徑賦值給log-lib
log )
#target_link_libraries()命令用於指定要關聯到的原生庫的庫
target_link_libraries(# 指定目標庫,與上面指定的函式庫名一致
native-lib
# 連結的庫,根據log-lib變數對應liblog.so函式庫
${log-lib} )
通過檢視native-lib.cpp方法,stringFromJNI目的是向Java層返回一個字串。如果要在native-lib.cpp檔案中新增新的方法,必須新增在extern”C” { } 中,或者在每個方法前加extern”C”, 否則會報找不到方法。如果原始檔為C,則須將extern“C”部分去掉,因為extern “C”的作用就是告訴編譯器以C方式編譯。
JNI開發列印日誌
#include <android/log.h>
#define LOG_TAG "System.out.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
LOG的使用
LOGD("TAGD,a=%d,b=%d",a,b);
LOGI("TAGI,a=%d,b=%d",a,b);
LOGW("TAGW,a=%d,b=%d",a,b);
LOGE("TAGE,a=%d,b=%d",a,b);
Android系統目前支援的CPU架構
ARMv5,ARMv7 (從2010年起)
x86 (從2011年起)
MIPS (從2012年起)
ARMv8,MIPS64和x86_64 (從2014年起)
每一個CPU架構對應一個ABI
CPU架構 ABI
ARMv5 ---> armeabi
ARMv7 ---> armeabi-v7a
x86 ---> x86
MIPS ---> mips
ARMv8 ---> arm64-v8a
MIPS64 ---> mips64
x86_64 ---> x86_64
armeabi:預設選項,將建立以基於ARM* v5TE 的裝置為目標的庫。 具有這種目標
的浮點運算使用軟體浮點運算。 使用此ABI(二進位制介面)建立的二進位制程式碼將可以
在所有 ARM*裝置上執行。所以armeabi通用性很強。但是速度慢
armeabi-v7a:建立支援基於ARM* v7 的裝置的庫,並將使用硬體FPU指令。
armeabi-v7a是針對有浮點運算或高階擴充套件功能的arm v7 cpu。
mips:MIPS是世界上很流行的一種RISC處理器。MIPS的意思是“無內部互鎖流水級
的微處理器”(Microprocessor without interlocked piped stages),其機
制是儘量利用軟體辦法避免流水線中的資料相關問題。
x86:支援基於硬體的浮點運算的IA-32 指令集。x86是可以相容armeabi平臺執行
的,無論是armeabi-v7a還是armeabi,同時帶來的也是效能上的損耗,另外需要
指出的是,打包出的x86的so,總會比armeabi平臺的體積更小。
總結
如果專案只包含了 armeabi,那麼在所有Android裝置都可以執行;
如果專案只包含了 armeabi-v7a,除armeabi架構的裝置外都可以執行;
如果專案只包含了 x86,那麼armeabi架構和armeabi-v7a的Android裝置是無法
執行的;
如果同時包含了 armeabi,armeabi-v7a和x86,所有裝置都可以執行,程式在運
行的時候去載入不同平臺對應的so,這是較為完美的一種解決方案,同時也會導致
包變大。