2019独角兽企业重金招聘Python工程师标准>>>
最近的项目涉及到JNI编程,经过一段时间的JNI编程之后,终于完美弄完了。所以,把在android studio中编译c/c++文件成.so库的过程记录一下。
在Android studio中使用cmake编译 .so库
1. 安装JNI(java Native Interface)的开发环境。
(1)NDK(Native Development kit):NDK是一个工具集,允许你的App使用一些底层语言代码,例如C和C++。
(2)CMake:CMake是一个跨平台的编译工具,可以用简单的语句来描述所有平台的编译过程,它能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。(谷歌从AndroidStudio2.2以上就添加了Cmake方式来编译NDK代码)
(3)LLDB:支持断点调试c/c++源码。
2. 在Android studio中,创建一个支持c/c++的project。
勾选上那个支持c++的选项:
3. project创建成功之后,会出现cmakelists.txt文件,文件中包含的指令:
(1)cmake的最低版本要求:
cmake_minimum_required(VERSION 3.4.1)
(2)使用 add_library()
的 CMake指令构建脚本添加源文件或库,为了确保 CMake 可以在编译时定位您的标头文件,您需要将 include_directories()
命令添加到 CMake 构建脚本中并指定标头的路径:
demo脚本:
add_library( # Sets the name of the library. my-native-lib #library的名称 # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp src/main/cpp/hello.cpp ) # Specifies a path to native header files. include_directories(src/main/cpp/include/)
(3)使用find_library添加 NDK库
Android NDK 提供了一套实用的原生 API 和库。通过将 NDK库包含到项目的 CMakeLists.txt
脚本文件中,您可以使用这些 API 中的任意一种。预构建的 NDK 库已经存在于 Android 平台上,因此,您无需再构建或将其封装到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,您甚至不需要在您的本地 NDK 安装中指定库的位置 - 只需要向 CMake 提供您希望使用的库的名称,并将其关联到您自己的原生库。
demo脚本:
find_library( # Sets the name of the path variable. log-lib #例如:添加ndk中log-lib库 # Specifies the name of the NDK library that # you want CMake to locate. log )
(4)为了确保创建的原生库可以使用log 库中的函数,需要使用 CMake 构建脚本中的 target_link_libraries()
命令关联库。
demo脚本:
target_link_libraries( # Specifies the target library. my-native-lib # Links the target library to the log library # included in the NDK. ${log-lib} )
完整的cmakelists.txt文件中的内容如下:
demo脚本:
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. my-native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp src/main/cpp/hello.cpp ) # Specifies a path to native header files. include_directories(src/main/cpp/include/) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. my-native-lib # Links the target library to the log library # included in the NDK. ${log-lib} )
一些截图:
其中,NDK 还以源代码的形式包含一些库,您在构建和关联到您的原生库时需要使用这些代码。您可以使用 CMake 构建脚本中的 add_library()
命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可以使用 ANDROID_NDK
路径变量,Android Studio 会自动为您定义此变量。
以下命令可以指示 CMake 构建 android_native_app_glue.c
,后者会将 NativeActivity
生命周期事件和触摸输入置于静态库中并将静态库关联到库中:
demo脚本:
add_library( app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ) # You need to link static libraries against your shared native library. target_link_libraries( my-native-lib app-glue ${log-lib} )
添加其他预构建库
添加预构建库与为 CMake 指定要构建的另一个原生库类似。不过,由于库已经预先构建,您需要使用 IMPORTED
标志告知 CMake 您只希望将库导入到项目中:
add_library( imported-lib SHARED IMPORTED )
然后,您需要使用 set_target_properties()
命令指定库的路径,如下所示。
某些库为特定的 CPU 架构(或应用二进制接口 (ABI))提供了单独的软件包,并将其组织到单独的目录中。此方法既有助于库充分利用特定的 CPU 架构,又能让您仅使用所需的库版本。要向 CMake 构建脚本中添加库的多个 ABI 版本,而不必为库的每个版本编写多个命令,您可以使用 ANDROID_ABI
路径变量。此变量使用 NDK 支持的一组默认 ABI,或者您手动配置 Gradle 而让其使用的一组经过筛选的 ABI。例如:
add_library(...) set_target_properties( # Specifies the target library. imported-lib # Specifies the parameter you want to define. PROPERTIES IMPORTED_LOCATION # Provides the path to the library you want to import. imported-lib/src/${ANDROID_ABI}/libimported-lib.so )
为了确保 CMake 可以在编译时定位您的标头文件,您需要使用 include_directories()
命令,并包含标头文件的路径:
include_directories( imported-lib/include/ )
注:如果您希望封装一个并不是构建时依赖项的预构建库(例如在添加属于 imported-lib
依赖项的预构建库时),则不需要执行以下说明来关联库。
要将预构建库关联到您自己的原生库,请将其添加到 CMake 构建脚本的 target_link_libraries()
命令中:
target_link_libraries( my-native-lib imported-lib app-glue ${log-lib} )
在您构建应用时,Gradle 会自动将导入的库封装到 APK 中。您可以使用 APK 分析器验证 Gradle 将哪些库封装到您的 APK 中。如需了解有关 CMake 命令的详细信息,请参阅 CMake 文档。
4. module中gradle文件的
(abi相关知识:https://developer.android.google.cn/ndk/guides/abis.html)
(1)默认配置中的externalNativeBuild的配置
defaultConfig { ... externalNativeBuild { cmake { cppFlags "" //配置c++的版本库,其中""表示使用默认的,如 cppFlags "-std=c++14" 为c++14版本 abiFilters "armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64" // 输出指定abi体系结构下的so库 } } }
(2)配置所引用的cmakelists.txt文件
externalNativeBuild { cmake { path "CMakeLists.txt" } }
完整的demo的gradle文件内容如下:
apply plugin: 'com.android.application' android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags "" //配置c++的版本库,其中""表示使用默认的,如 cppFlags "-std=c++14" 为c++14版本 abiFilters "armeabi", "armeabi-v7a", "x86","x86_64" // 输出指定abi体系结构下的so库 } } } ... //配置引用的CMakeLists.txt文件 externalNativeBuild { cmake { path "CMakeLists.txt" } } } dependencies { ... }
5. 加载编写的原生库
demo脚本:
static { System.loadLibrary("my-native-lib"); //通过静态块,加载原生库 } public native String stringFromJNI(); //定义一个native 方法
navity-lib.cpp的内容:
demo脚本:
#include#include extern "C" JNIEXPORT jstring JNICALL Java_com_youbale_cmakedemo_Mylib_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
6. build一下project,在build/intermediates/cmake目录下就可以找到编译完成的 .so库。(命名规则:lib+库的名称)
在android studio中把c/c++源码编译成.so文件就大功告成。