要为您的应用编译和调试原生代码,您须要如下组件:html
您能够使用 SDK 管理器安装这些组件:java
1.在打开的项目中,从菜单栏选择 Tools > Android > SDK Manager。android
2.点击 SDK Tools 标签。git
3.选中 LLDB、CMake 和 NDK 旁的复选框,如图 1 所示github
图 1. 从 SDK 管理器中安装 LLDB、CMake 和 NDK。api
4.点击 Apply,而后在弹出式对话框中点击 OK。架构
5.安装完成后,点击 Finish,而后点击 OK。oracle
建立支持原生代码的项目与建立任何其余 Android Studio 项目相似,不过前者还须要额外几个步骤:app
-fexceptions
标志添加到模块级 build.gradle
文件的 cppFlags
中,Gradle 会将其传递到 CMake。-frtti
标志添加到模块级 build.gradle
文件的 cppFlags
中,Gradle 会将其传递到 CMake。在 Android Studio 完成新项目的建立后,请从 IDE 左侧打开 Project 窗格并选择 Android 视图。如图 2 中所示,Android Studio 将添加 cpp 和 External Build Files 组:框架
图 2. 您的原生源文件和外部构建脚本的 Android 视图组。
注:此视图没法反映磁盘上的实际文件层次结构,而是将类似文件分到一组中,简化项目导航。
native-lib.cpp
,并将其置于应用模块的 src/main/cpp/
目录中。本示例代码提供了一个简单的 C++ 函数 stringFromJNI()
,此函数能够返回字符串“Hello from C++”。要了解如何向项目添加其余源文件,请参阅介绍如何建立新的原生源文件的部分。build.gradle
文件指示 Gradle 如何构建应用同样,CMake 和 ndk-build 须要一个构建脚原本了解如何构建您的原生库。对于新项目,Android Studio 会建立一个 CMake 构建脚本 CMakeLists.txt
,并将其置于模块的根目录中。要详细了解此构建脚本的内容,请参阅介绍如何建立 Cmake 构建脚本的部分。点击 Run 后,Android Studio 将在您的 Android 设备或者模拟器上构建并启动一个显示文字“Hello from C++”的应用。下面的概览介绍了构建和运行示例应用时会发生的事件:
CMakeLists.txt
。native-lib.cpp
编译到共享的对象库中,并命名为 libnative-lib.so
,Gradle 随后会将其打包到 APK 中。MainActivity
会使用 System.loadLibrary()
加载原生库。如今,应用可使用库的原生函数 stringFromJNI()
。MainActivity.onCreate()
调用 stringFromJNI()
,这将返回“Hello from C++”并使用这些文字更新 TextView
。注:Instant Run 与使用原生代码的项目不兼容。Android Studio 会自动停用此功能。
若是您想要验证 Gradle 是否已将原生库打包到 APK 中,可使用 APK 分析器:
1.选择 Build > Analyze APK。
2.从 app/build/outputs/apk/
目录中选择 APK 并点击 OK。
3.如图 3 中所示,您会在 APK 分析器窗口的 lib/<ABI>/
下看到 libnative-lib.so
图 3. 使用 APK 分析器定位原生库。
提示:若是您想要试验使用原生代码的其余 Android 应用,请点击 File > New > Import Sample 并从 Ndk 列表中选择示例项目。
若是您但愿向现有项目添加原生代码,请执行如下步骤:
CMakeLists.txt
构建脚本或者使用 ndk-build 并包含 Android.mk
构建脚本,则能够跳过此步骤。配置完项目后,您可使用 JNI 框架从 Java 代码中访问您的原生函数。要构建和运行应用,只需点击 Run 。Gradle 会以依赖项的形式添加您的外部原生构建流程,用于编译、构建原生库并将其随 APK 一块儿打包。
要在应用模块的主源代码集中建立一个包含新建原生源文件的 cpp/
目录,请按如下步骤操做:
cpp
)并点击 OK。native-lib
。.cpp
。
.cxx
或 .hxx
。在弹出的 C/C++ 对话框中,从 Source Extension 和 Header Extension 下拉菜单中选择另外一个文件扩展名,而后点击 OK。若是您的原生源文件尚未 CMake 构建脚本,则您须要自行建立一个并包含适当的 CMake 命令。CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt
。本部分介绍了您应包含到构建脚本中的一些基本命令,用于在建立原生库时指示 CMake 应使用哪些源文件。
注:若是您的项目使用 ndk-build,则不须要建立 CMake 构建脚本。提供一个指向您的 Android.mk
文件的路径,将 Gradle 关联到您的原生库。
要建立一个能够用做 CMake 构建脚本的纯文本文件,请按如下步骤操做:
注:您能够在所需的任意位置建立构建脚本。不过,在配置构建脚本时,原生源文件和库的路径将与构建脚本的位置相关。
如今,您能够添加 CMake 命令,对您的构建脚本进行配置。要指示 CMake 从原生源代码建立一个原生库,请将 cmake_minimum_required()
和 add_library()
命令添加到您的构建脚本中:
# Sets the minimum version of CMake required to build your native library. # This ensures that a certain set of CMake features is available to # your build. cmake_minimum_required(VERSION 3.4.1) # Specifies a library name, specifies whether the library is STATIC or # SHARED, and provides relative paths to the source code. You can # define multiple libraries by adding multiple add.library() commands, # and CMake builds them for you. When you build your app, Gradle # automatically packages shared libraries with your APK. add_library( # Specifies the name of the library. 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 )
使用 add_library()
向您的 CMake 构建脚本添加源文件或库时,Android Studio 还会在您同步项目后在 Project 视图下显示关联的标头文件。不过,为了确保 CMake 能够在编译时定位您的标头文件,您须要将 include_directories()
命令添加到 CMake 构建脚本中并指定标头的路径:
add_library(...)
# Specifies a path to native header files.
include_directories(src/main/cpp/include/)
CMake 使用如下规范来为库文件命名:
lib库名称.so
例如,若是您在构建脚本中指定“native-lib”做为共享库的名称,CMake 将建立一个名称为 libnative-lib.so
的文件。不过,在 Java 代码中加载此库时,请使用您在 CMake 构建脚本中指定的名称:
static { System.loadLibrary(“native-lib”); }
注:若是您在 CMake 构建脚本中重命名或移除某个库,您须要先清理项目,Gradle 随后才会应用更改或者从 APK 中移除旧版本的库。要清理项目,请从菜单栏中选择 Build > Clean Project。
Android Studio 会自动将源文件和标头添加到 Project 窗格的 cpp 组中。使用多个 add_library()
命令,您能够为 CMake 定义要从其余源文件构建的更多库。
Android NDK 提供了一套实用的原生 API 和库。经过将 NDK 库包含到项目的 CMakeLists.txt
脚本文件中,您可使用这些 API 中的任意一种。
预构建的 NDK 库已经存在于 Android 平台上,所以,您无需再构建或将其打包到 APK 中。因为 NDK 库已是 CMake 搜索路径的一部分,您甚至不须要在您的本地 NDK 安装中指定库的位置 - 只须要向 CMake 提供您但愿使用的库的名称,并将其关联到您本身的原生库。
将 find_library()
命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。您可使用此变量在构建脚本的其余部分引用 NDK 库。如下示例能够定位 Android 特定的日志支持库并将其路径存储在 log-lib
中:
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 )
为了确保您的原生库能够在 log
库中调用函数,您须要使用 CMake 构建脚本中的 target_link_libraries()
命令关联库:
find_library(...) # Links your native library against one or more other native libraries. target_link_libraries( # Specifies the target library. native-lib # Links the log library to the target library. ${log-lib} )
NDK 还以源代码的形式包含一些库,您在构建和关联到您的原生库时须要使用这些代码。您可使用 CMake 构建脚本中的 add_library()
命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可使用 ANDROID_NDK
路径变量,Android Studio 会自动为您定义此变量。
如下命令能够指示 CMake 构建 android_native_app_glue.c
,后者会将 NativeActivity
生命周期事件和触摸输入置于静态库中并将静态库关联到 native-lib
:
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( 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.
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libimported-lib.so ) #CMAKE_SOURCE_DIR 变量为当前cmake文件所在的目录
为了确保 CMake 能够在编译时定位您的标头文件,您须要使用 include_directories()
命令,并包含标头文件的路径:
include_directories( imported-lib/include/ )
注:若是您但愿打包一个并非构建时依赖项的预构建库(例如在添加属于 imported-lib
依赖项的预构建库时),则不须要执行如下说明来关联库。
要将预构建库关联到您本身的原生库,请将其添加到 CMake 构建脚本的 target_link_libraries()
命令中:
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )
在您构建应用时,Gradle 会自动将导入的库打包到 APK 中。您可使用 APK 分析器验证 Gradle 将哪些库打包到您的 APK 中。如需了解有关 CMake 命令的详细信息,请参阅 CMake 文档。
要将 Gradle 关联到您的原生库,您须要提供一个指向 CMake 或 ndk-build 脚本文件的路径。在您构建应用时,Gradle 会以依赖项的形式运行 CMake 或 ndk-build,并将共享的库打包到您的 APK 中。Gradle 还使用构建脚原本了解要将哪些文件添加到您的 Android Studio 项目中,以便您能够从 Project 窗口访问这些文件。若是您的原生源文件没有构建脚本,则须要先建立 CMake 构建脚本,而后再继续。
将 Gradle 关联到原生项目后,Android Studio 会更新 Project 窗格以在 cpp 组中显示您的源文件和原生库,在 External Build Files 组中显示您的外部构建脚本。
注:更改 Gradle 配置时,请确保经过点击工具栏中的 Sync Project 应用更改。此外,若是在将 CMake 或 ndk-build 脚本文件关联到 Gradle 后再对其进行更改,您应当从菜单栏中选择 Build > Refresh Linked C++ Projects,将 Android Studio 与您的更改同步。
您可使用 Android Studio UI 将 Gradle 关联到外部 CMake 或 ndk-build 项目:
1.从 IDE 左侧打开 Project 窗格并选择 Android 视图。
2.右键点击您想要关联到原生库的模块(例如 app 模块),并从菜单中选择 Link C++ Project with Gradle。您应看到一个如图 4 所示的对话框。
3.从下拉菜单中,选择 CMake 或 ndk-build。
1.若是您选择 CMake,请使用 Project Path 旁的字段为您的外部 CMake 项目指定 CMakeLists.txt
脚本文件。
2.若是您选择 ndk-build,请使用 Project Path 旁的字段为您的外部 ndk-build 项目指定 Android.mk
脚本文件。若是 Application.mk
文件与您的 Android.mk
文件位于相同目录下,Android Studio 也会包含此文件。
图 4. 使用 Android Studio 对话框关联外部 C++ 项目
4.点击ok。
要手动配置 Gradle 以关联到您的原生库,您须要将 externalNativeBuild {}
块添加到模块级 build.gradle
文件中,并使用 cmake {}
或 ndkBuild {}
对其进行配置:
android { ... defaultConfig {...} buildTypes {...} // Encapsulates your external native build configurations. externalNativeBuild { // Encapsulates your CMake build configurations. cmake { // Provides a relative path to your CMake build script. path "CMakeLists.txt" } } }
注:若是您想要将 Gradle 关联到现有 ndk-build 项目,请使用 ndkBuild {}
块而不是 cmake {}
,并提供 Android.mk
文件的相对路径。若是 Application.mk
文件与您的 Android.mk
文件位于相同目录下,Gradle 也会包含此文件。
您能够在模块级 build.gradle
文件的 defaultConfig {}
块中配置另外一个 externalNativeBuild {}
块,为 CMake 或 ndk-build 指定可选参数和标志。与 defaultConfig {}
块中的其余属性相似,您也能够在构建配置中为每一个产品风味重写这些属性。
例如,若是您的 CMake 或 ndk-build 项目定义多个原生库,您可使用 targets
属性仅为给定产品风味构建和打包这些库中的一部分。如下代码示例说明了您能够配置的部分属性:
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 ndkBuild {} cmake { // Passes optional arguments to CMake. arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang" // Sets optional flags for the C compiler. cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2" // Sets a flag to enable format macro constants for the C++ compiler. cppFlags "-D__STDC_FORMAT_MACROS" } } } buildTypes {...} productFlavors { ... demo { ... externalNativeBuild { cmake { ... // Specifies which native libraries to build and package for this // product flavor. If you don't configure this property, Gradle // builds and packages all shared object libraries that you define // in your CMake or ndk-build project. targets "native-lib-demo" } } } paid { ... externalNativeBuild { cmake { ... targets "native-lib-paid" } } } } // Use this block to link Gradle to your CMake or ndk-build script. externalNativeBuild { cmake {...} // or ndkBuild {...} } }
要详细了解配置产品风味和构建变体,请参阅配置构建变体。如需了解您可使用 arguments
属性为 CMake 配置的变量列表,请参阅使用 CMake 变量。
默认状况下,Gradle 会针对 NDK 支持的 ABI 将您的原生库构建到单独的 .so
文件中,并将其所有打包到您的 APK 中。若是您但愿 Gradle 仅构建和打包原生库的特定 ABI 配置,您能够在模块级 build.gradle
文件中使用 ndk.abiFilters
标志指定这些配置,以下所示:
android { ... defaultConfig { ... externalNativeBuild { cmake {...} // or ndkBuild {...} } 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 {...} }
在大多数状况下,您只须要在 ndk {}
块中指定 abiFilters
(如上所示),由于它会指示 Gradle 构建和打包原生库的这些版本。不过,若是您但愿控制 Gradle 应当构建的配置,并独立于您但愿其打包到 APK 中的配置,请在 defaultConfig.externalNativeBuild.cmake {}
块(或 defaultConfig.externalNativeBuild.ndkBuild {}
块中)配置另外一个 abiFilters
标志。Gradle 会构建这些 ABI 配置,不过仅会打包您在 defaultConfig.ndk{}
块中指定的配置。
为了进一步下降 APK 的大小,请考虑配置 ABI APK 拆分,而不是建立一个包含原生库全部版本的大型 APK,Gradle 会为您想要支持的每一个 ABI 建立单独的 APK,而且仅打包每一个 ABI 须要的文件。若是您配置 ABI 拆分,但没有像上面的代码示例同样指定 abiFilters
标志,Gradle 会构建原生库的全部受支持 ABI 版本,不过仅会打包您在 ABI 拆分配置中指定的版本。为了不构建您不想要的原生库版本,请为 abiFilters
标志和 ABI 拆分配置提供相同的 ABI 列表。
Demo:
https://github.com/derek022/voicer/blob/master/app/CMakeLists.txt