【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )
文章目錄
- I . CMake 引入動態庫與靜態庫區別
- II . Android Studio 中 CMake 引入動態庫流程
- III . 指定動態庫查找路徑
- IV . 鏈接函數庫
- V . 完整代碼示例
- 1 . Module 級別的 build.gradle 完整配置代碼
- 2 . CMakeList.txt 完整配置代碼
- 3 . Java 層代碼
- 4 . C++ 層代碼
- 5 . 運行結果
- VI . 博客資源
參考博客 : 【Android NDK 開發】Android Studio 使用 CMake 導入靜態庫 ( CMake 簡介 | 構建腳本路徑配置 | 引入靜態庫 | 指定靜態庫路徑 | 鏈接動態庫 )
I . CMake 引入動態庫與靜態庫區別
1 . CMake 引入靜態庫 : 使用 add_library() 導入靜態庫 , set_target_properties() 設置靜態庫路徑 ;
# 引入靜態庫 # ① 參數 1 ( add ) : 設置引入的靜態庫名稱 # ② 參數 2 ( SHARED ) : 設置引入的函數庫類型 : ① 靜態庫 STATIC ② 動態庫 SHARED # ③ 參數 3 ( IMPORTED ) : 表示引入第三方靜態庫 , 導入靜態庫 , 相當于預編譯靜態庫 # 后續還需要設置導入路徑 , 配合該配置使用 add_library(# 設置引入的靜態庫名稱add# 設置引入的函數庫類型為靜態庫STATIC# 表示引入第三方靜態庫IMPORTED)# 設置上述靜態庫的導入路徑 # 設置目標屬性參數 : # ① 參數 1 ( add ) : 要設置哪個函數庫的屬性 # ② 參數 2 ( PROPERTIES ) : 設置目標屬性 # ③ 參數 3 ( IMPORTED_LOCATION ) : 設置導入路徑 # ④ 參數 4 : 配置靜態庫的文件路徑 set_target_properties(# 設置目標add# 設置屬性PROPERTIES# 導入路徑IMPORTED_LOCATION# ${CMAKE_SOURCE_DIR} 是本 CMakeList.txt 構建腳本的路徑 , 是 CMake 工具內置的變量# Android CMake 也內置了一些變量 , 如 ANDROID_ABI${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a/libadd.a)使用上面的方式引入動態庫會出現于 Android.mk 配置一樣的問題 , 6.0 以上的 Android 系統在運行時出現找不到路徑的問題 ;
如果引用動態庫 , 則不能用這種方式 , 要使用下面的動態庫引入方式 ;
2 . CMake 引入動態庫 : 使用 set() , 指定一個 CMAKE_CXX_FLAGS 編譯器參數 , 在編譯器參數后添加 -L 參數指定動態庫查找目錄 ;
# 設置變量 # CMAKE_CXX_FLAGS 表示會將 C++ 的參數傳給編譯器 # CMAKE_C_FLAGS 表示會將 C 參數傳給編譯器# 參數設置 : 傳遞 CMAKE_CXX_FLAGS C+= 參數給編譯器時 , 在 該參數后面指定庫的路徑 # CMAKE_SOURCE_DIR 指的是當前的文件地址 # -L 參數指定動態庫的查找路徑 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}")原理參考 : 【Android NDK 開發】NDK 交叉編譯 ( NDK 函數庫目錄 | Linux 交叉編譯環境搭建 | 指定頭文件目錄 | 指定函數庫目錄 | 編譯 Android 命令行可執行文件 ) : V . 指定編譯的庫文件
II . Android Studio 中 CMake 引入動態庫流程
Android Studio 中 CMake 引入靜態庫流程 :
1 . build.gradle 配置 CMake 編譯選項 : 在 Module 級別的 build.gradle 腳本中配置 CMake 編譯選項 ;
// I . NDK 配置 1 : 配置 AS 工程中的 C/C++ 源文件的編譯// defaultConfig 內部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件編譯參數// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 構建腳本的路徑externalNativeBuild {cmake {cppFlags ""//配置編譯 C/C++ 源文件為哪幾個 CPU 指令集的函數庫 (arm , x86 等)abiFilters "armeabi-v7a"}/*ndkBuild{abiFilters "armeabi-v7a" *//*, "arm64-v8a", "x86", "x86_64"*//*}*/}2 . build.gradle 配置 NDK 打包選項 : 在 Module 級別的 build.gradle 腳本中配置 NDK 打包選項 ;
// II . NDK 配置 2 : 配置 AS 工程中的 C/C++ 源文件的編譯//配置 APK 打包 哪些動態庫// 示例 : 如在工程中集成了第三方庫 , 其提供了 arm, x86, mips 等指令集的動態庫// 那么為了控制打包后的應用大小, 可以選擇性打包一些庫 , 此處就是進行該配置ndk{// 打包生成的 APK 文件指揮包含 ARM 指令集的動態庫abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/}3 . build.gradle 配置 CMake 構建腳本 CMakeList.txt 路徑 : 在 Module 級別的 build.gradle 腳本中配置 Android.mk 構建腳本的路徑 ;
// III . NDK 配置 : 配置 AS 工程中的 C/C++ 源文件的編譯構建腳本// 配置 NDK 的編譯腳本路徑// 編譯腳本有兩種 ① CMakeList.txt ② Android1.mk// defaultConfig 內部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件編譯參數// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 構建腳本的路徑externalNativeBuild {// 配置 CMake 構建腳本 CMakeLists.txt 腳本路徑cmake {path "src/main/cpp/CMakeLists.txt"version "3.10.2"}// 配置 Android1.mk 構建腳本路徑/*ndkBuild{//path "src/main/ndkBuild_Shared/Android.mk"path "src/main/ndkBuild_Static/Android.mk"}*/}4 . CMake 構建腳本 CMakeList.txt 設置動態庫查找路徑 :
# 設置變量 # CMAKE_CXX_FLAGS 表示會將 C++ 的參數傳給編譯器 # CMAKE_C_FLAGS 表示會將 C 參數傳給編譯器# 參數設置 : 傳遞 CMAKE_CXX_FLAGS C+= 參數給編譯器時 , 在 該參數后面指定庫的路徑 # CMAKE_SOURCE_DIR 指的是當前的文件地址 # -L 參數指定動態庫的查找路徑 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}")5 . CMake 構建腳本 CMakeList.txt 鏈接靜態庫 :
# 鏈接函數庫 # 參數 1 : 本構建腳本要生成的動態庫目 標 # 參數 2 ~ ... : 后面是之前預編譯的動態庫或靜態庫 , 或引入的動態庫 target_link_libraries(native-lib# 表示 編譯 native-lib 模塊, 要鏈接 add 模塊add${log-lib})III . 指定動態庫查找路徑
導入第三方函數庫路徑配置 : 通過設置編譯器參數方式實現 ;
① 編譯器類型 : CMAKE_CXX_FLAGS 表示 C++ 編譯器參數 , CMAKE_C_FLAGS 表示 C 編譯器參數 ;
② 參數追加 : set 語句 , 在 CMAKE_CXX_FLAGS 編譯器參數后 , 追加了 “-L${CMAKE_SOURCE_DIR}/…/jniLibs/${ANDROID_ABI}” 內容 ;
# 設置變量 # CMAKE_CXX_FLAGS 表示會將 C++ 的參數傳給編譯器 # CMAKE_C_FLAGS 表示會將 C 參數傳給編譯器# 參數設置 : 傳遞 CMAKE_CXX_FLAGS C+= 參數給編譯器時 , 在 該參數后面指定庫的路徑 # CMAKE_SOURCE_DIR 指的是當前的文件地址 # -L 參數指定動態庫的查找路徑 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}")原理參考 : 【Android NDK 開發】NDK 交叉編譯 ( NDK 函數庫目錄 | Linux 交叉編譯環境搭建 | 指定頭文件目錄 | 指定函數庫目錄 | 編譯 Android 命令行可執行文件 ) : V . 指定編譯的庫文件
IV . 鏈接函數庫
鏈接函數庫 : 這里注意第一個參數必須是要生成的動態庫模塊 ;
# 鏈接函數庫 # 參數 1 : 本構建腳本要生成的動態庫目標 # 參數 2 ~ ... : 后面是之前預編譯的動態庫或靜態庫 , 或引入的動態庫 target_link_libraries(native-lib# 表示 編譯 native-lib 模塊, 要鏈接 add 模塊add${log-lib})V . 完整代碼示例
1 . Module 級別的 build.gradle 完整配置代碼
apply plugin: 'com.android.application'android {compileSdkVersion 29buildToolsVersion "29.0.0"defaultConfig {applicationId "kim.hsl.cmake"minSdkVersion 15targetSdkVersion 29versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"// I . NDK 配置 1 : 配置 AS 工程中的 C/C++ 源文件的編譯// defaultConfig 內部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件編譯參數// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 構建腳本的路徑externalNativeBuild {cmake {cppFlags ""//配置編譯 C/C++ 源文件為哪幾個 CPU 指令集的函數庫 (arm , x86 等)abiFilters "armeabi-v7a"}/*ndkBuild{abiFilters "armeabi-v7a" *//*, "arm64-v8a", "x86", "x86_64"*//*}*/}// II . NDK 配置 2 : 配置 AS 工程中的 C/C++ 源文件的編譯//配置 APK 打包 哪些動態庫// 示例 : 如在工程中集成了第三方庫 , 其提供了 arm, x86, mips 等指令集的動態庫// 那么為了控制打包后的應用大小, 可以選擇性打包一些庫 , 此處就是進行該配置ndk{// 打包生成的 APK 文件指揮包含 ARM 指令集的動態庫abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/}}// III . NDK 配置 : 配置 AS 工程中的 C/C++ 源文件的編譯構建腳本// 配置 NDK 的編譯腳本路徑// 編譯腳本有兩種 ① CMakeList.txt ② Android1.mk// defaultConfig 內部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件編譯參數// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 構建腳本的路徑externalNativeBuild {// 配置 CMake 構建腳本 CMakeLists.txt 腳本路徑cmake {path "src/main/cpp/CMakeLists.txt"version "3.10.2"}// 配置 Android1.mk 構建腳本路徑/*ndkBuild{//path "src/main/ndkBuild_Shared/Android.mk"path "src/main/ndkBuild_Static/Android.mk"}*/}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}} }dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.constraintlayout:constraintlayout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'androidx.test:runner:1.2.0'androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' }
2 . CMakeList.txt 完整配置代碼
# 指定 CMake 最低版本 cmake_minimum_required(VERSION 3.4.1)# 設置函數庫編譯 add_library( # 參數 1 : 設置生成的動態庫名稱native-lib# 參數 2 : 設置生成的函數庫類型 : ① 靜態庫 STATIC ② 動態庫 SHAREDSHARED# 參數 3 : 配置要編譯的源文件native-lib.cpp)# 使用下面的方式引入動態庫會出現于 Android.mk 配置一樣的問題 , 6.0 以上的 Android 系統在運行時出現找不到路徑的問題# 引入動態庫 #add_library(add SHARED IMPORTED) # 設置函數庫的導入路徑 #set_target_properties(add PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a/libadd.so)# 打印日志信息 # ${ANDROID_ABI} 的作用是獲取當前的 CPU 指令集架構 # 當本次編譯 armeabi-v7a CPU 架構時 , ${ANDROID_ABI} 值為 armeabi-v7a # 當本次編譯 x86 CPU 架構時 , ${ANDROID_ABI} 值為 x86 message("CMAKE_SOURCE_DIR : ${CMAKE_SOURCE_DIR}, ANDROID_ABI : ${ANDROID_ABI}")# 到預設的目錄查找 log 庫 , 將找到的路徑賦值給 log-lib # 這個路徑是 NDK 的 ndk-bundle\platforms\android-29\arch-arm\usr\lib\liblog.so # 不同的 Android 版本號 和 CPU 架構 需要到對應的目錄中查找 , 此處是 29 版本 32 位 ARM 架構的日志庫 # # 可以不配置 : # 可以不進行該配置, 直接在后面的 target_link_libraries 中鏈接 log 也不會出錯 find_library(log-liblog)# 打印日志庫位置 message(${log-lib})# 設置變量 # CMAKE_CXX_FLAGS 表示會將 C++ 的參數傳給編譯器 # CMAKE_C_FLAGS 表示會將 C 參數傳給編譯器# 參數設置 : 傳遞 CMAKE_CXX_FLAGS C+= 參數給編譯器時 , 在 該參數后面指定庫的路徑 # CMAKE_SOURCE_DIR 指的是當前的文件地址 # -L 參數指定動態庫的查找路徑 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}")# 鏈接函數庫 # 參數 1 : 本構建腳本要生成的動態庫目標 # 參數 2 ~ ... : 后面是之前預編譯的動態庫或靜態庫 , 或引入的動態庫 target_link_libraries(native-lib# 表示 編譯 native-lib 模塊, 要鏈接 add 模塊add${log-lib})
3 . Java 層代碼
package kim.hsl.cmake;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.widget.TextView;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.sample_text);tv.setText(stringFromJNI());}public native String stringFromJNI(); }
4 . C++ 層代碼
#include <jni.h>#include <stdio.h> #include <stdlib.h>#include <android/log.h>//調用 libadd.so 動態庫中的方法 extern "C" { //注意在 C++ 中調用 C 語言方法 , 需要做兼容設置extern int add(int a, int b); }extern "C" JNIEXPORT jstring JNICALL Java_kim_hsl_cmake_MainActivity_stringFromJNI(JNIEnv *env,jobject obj) {//調用動態庫中的函數int sum = add(1, 2);// Logcat 打印計算結果__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "Native Caculate (CMake Build) : %d + %d = %d", 1, 2, sum);//將加法運算轉為字符串 , 字符串如果不夠長就報錯 ...char str[100] = "0";//字符串格式化sprintf(str, "Native Caculate : Shared Library (CMake Build) : %d + %d = %d", 1, 2, sum);return env->NewStringUTF(str); }
5 . 運行結果
VI . 博客資源
博客相關資源 :
① CSDN 博客地址 : 【Android NDK 開發】Android Studio 使用 CMake 導入動態庫 ( 構建腳本路徑配置 | 指定動態庫查找路徑 | 鏈接動態庫 )
② 博客資源下載地址 : https://download.csdn.net/download/han1202012/12162546
③ 示例代碼 GitHub 地址 : https://github.com/han1202012/009_NDK_CMake_Shared
總結
以上是生活随笔為你收集整理的【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android NDK 开发】Andr
- 下一篇: 【Android FFMPEG 开发】F