NDK(23) 使用CMake 构建 c/c++代码库
1.官网
https://developer.android.com/studio/projects/add-native-code.html
https://developer.android.google.cn/ndk/guides/cmake
https://developer.android.google.cn/studio/projects/configure-cmake
2.android studio 安装相关工具
- 在打开的项目中,从菜单栏选择 Tools > Android > SDK Manager。
- 点击 SDK Tools 标签。
- 选中 LLDB、CMake 和 NDK 旁的复选框,如图所示.
3.新建支持c/c++的项目
- 在android studio 3.4.1 新建向导里选 Native C++ 模板.
2.选择想要使用的c++标准,如C++14.
3.最后 生成的项目如下:
其中:
-
- CMakeLists.txt 是cmake构建工程要用的项目配置文件.
- native-lib.cpp是源文件
4.编辑代码,使用本地程序....
4.在现有项目中添加 c/c++库
4.1 创建 jni目录
项目名(或者File) ---> 右键 ---> New ---> Folder ---> JNI Folder
4.2 创建 CMakeLists.txt文件
在上一步上创建的jni目录(当前版本目录名是cpp) 右键 ---> New ---> File 输入CMakeLists.txt
4.3 配置CMakeLists.txt 文件
1 # Sets the minimum version of CMake required to build your native library. 2 # This ensures that a certain set of CMake features is available to 3 # your build. 4 5 cmake_minimum_required(VERSION 3.10.2) 6 7 #set(CMAKE_SYSTEM_NAME Android) 8 #set(CMAKE_SYSTEM_VERSION 21) # API level 9 #set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) 10 #set(CMAKE_ANDROID_NDK /home/Jei/android/sdk/ndk/22.1.7171670) 11 #set(CMAKE_ANDROID_STL_TYPE gnustl_static) 12 13 add_library (nal SHARED test.c native.cpp) 14 15 include_directories (include/crypto) 16 add_library (crypto STATIC IMPORTED) 17 set_target_properties (crypto PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcrypto.a) 18 19 include_directories (include/openssl) 20 add_library (openssl STATIC IMPORTED) 21 set_target_properties (openssl PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libssl.a) 22 23 include_directories (include/curl) 24 add_library (curl STATIC IMPORTED) 25 set_target_properties (curl PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcurl.a) 26 27 find_library (log-lib log) 28 29 target_link_libraries (nal crypto openssl curl ${log-lib} )
- 注意CMakeLists.txt中源文件和include的路径引用问题,这里是和源文件同级.
- CMakeLists.txt 常用函数含义
- 配置cmake见官方文档: https://developer.android.google.cn/studio/projects/configure-cmake
- 下面是常用的cmake命令.
cmake命令官网 : https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html
set |
设置宏 |
add_library() |
向您的 CMake 构建脚本添加源文件或库 1 add_library( # Specifies the name of the library. 2 native-lib 3 4 # Sets the library as a shared library. 5 SHARED 6 7 # Provides a relative path to your source file(s). 8 src/main/cpp/native-lib.cpp ) |
include_directories |
指定头文件的路径 1 add_library(...) 2 3 # Specifies a path to native header files. 4 include_directories(src/main/cpp/include/) |
find_library() |
添加引用的NDK 库 1 find_library( # Defines the name of the path variable that stores the 2 # location of the NDK library. 3 log-lib 4 5 # Specifies the name of the NDK library that 6 # CMake needs to locate. 7 log ) |
target_link_libraries() |
链接多个库 1 target_link_libraries( native-lib imported-lib app-glue ${log-lib} ) |
add_library() |
将其它源代码编译到本地库中. 1 add_library( app-glue 2 STATIC 3 ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) 4 5 # You need to link static libraries against your shared native library. 6 target_link_libraries( native-lib app-glue ${log-lib} ) |
set_target_properties 和 IMPORTED |
IMPORTED不是个函数,只是add_library的参数,添加其他预构建的本地库 然后,您需要使用 set_target_properties() 命令指定库的路径. |
4.4 关联Gradle与 CMakeLists.txt
右键点击您想要关联的模块(例如 app 模块),并从菜单中选择 Link C++ Project with Gradle。关联到之前创建的CMakeLists.txt,点OK.
4.5 完成本地的c/c++代码工作
MainActivity.java
1 public class MainActivity extends AppCompatActivity { 2 3 // Used to load the 'native-lib' library on application startup. 4 static { 5 System.loadLibrary("native-lib"); 6 } 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.activity_main); 12 13 // Example of a call to a native method 14 TextView tv = findViewById(R.id.sample_text); 15 tv.setText(stringFromJNI()); 16 } 17 18 /** 19 * A native method that is implemented by the 'native-lib' native library, 20 * which is packaged with this application. 21 */ 22 public native String stringFromJNI(); 23 }
native-lib.cpp
1 #include <jni.h> 2 #include <string> 3 4 extern "C" JNIEXPORT jstring JNICALL 5 Java_com_example_cpp14_MainActivity_stringFromJNI( 6 JNIEnv* env, 7 jobject /* this */) { 8 std::string hello = "Hello from C++"; 9 return env->NewStringUTF(hello.c_str()); 10 }
kotlin版本:
1 class NotifyService : Service() { 2 external fun native() 3 init{ 4 System.loadLibrary("alive-lib") 5 } 6 fun cantStartRemoteService(){ 7 Logger.e("NotifyService","cantStartRemoteService") 8 //... 9 } 10 ... 11 }
native层:
1 #include <jni.h> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <android/log.h> 6 7 8 #define LOGE(TAG,...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 9 10 extern "C" 11 JNIEXPORT void JNICALL 12 Java_com_example_sjjg_alive_NotifyService_native(JNIEnv *env, jobject thiz) { 13 printf("hello native"); 14 LOGE("alive-lib","NotifyService_native"); 15 jclass clz = env->GetObjectClass(thiz); 16 17 /*fun cantStartRemoteService()*/ 18 jmethodID method = env->GetMethodID(clz,"cantStartRemoteService","()V"); 19 env->CallVoidMethod(thiz,method); 20 }
5.在 Gradle 中使用 CMake 变量
https://developer.android.com/ndk/guides/cmake.html#variables
1 android { 2 ... 3 defaultConfig { 4 ... 5 // This block is different from the one you use to link Gradle 6 // to your CMake or ndk-build script. 7 externalNativeBuild { 8 9 // For ndk-build, instead use ndkBuild {} 10 cmake { 11 12 // Passes optional arguments to CMake. 13 arguments "-DANDROID_TOOLCHAIN=clang","--D__ANDROID_API__=30","-DANDROID_STL=c++_static" 14 15 // Sets optional flags for the C compiler. 16 cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2" 17 18 // Sets a flag to enable format macro constants for the C++ compiler. 19 cppFlags "-fexceptions", "-frtti","-std=c++11" 20 } 21 } 22 } 23 24 buildTypes {...} 25 26 ... 27 }
6.指定本地库的cpu架构
默认情况下,Gradle 会针对 NDK 支持的 ABI 将您的原生库构建到单独的 .so
文件中,并将其全部打包到您的 APK 中。如果您希望 Gradle 仅构建和打包原生库的特定 ABI 配置,您可以在模块级 build.gradle
文件中使用 ndk.abiFilters
标志指定这些配置.
1 apply plugin: 'com.android.application' 2 3 android { 4 compileSdkVersion 28 5 defaultConfig { 6 applicationId "com.example.tocpp5" 7 minSdkVersion 15 8 targetSdkVersion 28 9 versionCode 1 10 versionName "1.0" 11 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 // This block is different from the one you use to link Gradle 13 // to your CMake or ndk-build script. 14 externalNativeBuild { 15 // For ndk-build, instead use ndkBuild {} 16 cmake { 17 // Passes optional arguments to CMake. 18 arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang" 19 20 // Sets optional flags for the C compiler. 21 cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2" 22 23 // Sets a flag to enable format macro constants for the C++ compiler. 24 cppFlags "-D__STDC_FORMAT_MACROS" 25 26 abiFilters 'armeabi-v7a','arm64-v8a' 27 28 } 29 } 30 ndk { 31 // Specifies the ABI configurations of your native 32 // libraries Gradle should build and package with your APK. 33 abiFilters 'x86', 'x86_64', 'armeabi-v7a','arm64-v8a' 34 } 35 } 36 buildTypes { 37 release { 38 minifyEnabled false 39 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 40 } 41 } 42 externalNativeBuild { 43 cmake { 44 path file('src/main/jni/CMakeLists.txt') 45 } 46 } 47 } 48 49 dependencies { 50 implementation fileTree(dir: 'libs', include: ['*.jar']) 51 implementation 'androidx.appcompat:appcompat:1.0.2' 52 implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 53 testImplementation 'junit:junit:4.12' 54 androidTestImplementation 'androidx.test:runner:1.2.0' 55 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 56 }
在 defaultConfig.externalNativeBuild.cmake {}
块
或
defaultConfig.externalNativeBuild.ndkBuild {}
块
中配置另一个 abiFilters
标志。Gradle 会构建defaultConfig.ndk中的 ABI 配置,不过仅会打包在 defaultConfig.
块中指定的配置。cmake或者
{}ndkBuild
注: 非特殊情况下,只保留 'arm64-v8a' (64位)和 'armeabi-v7a' (32位) 就够用了。 参考微信的apk。
7.NDK中支持的库
https://developer.android.google.cn/ndk/guides/stable_apis