作为一个菜鸟,一直没有接触和学习NDK,和JNI方面的知识,趁着最近项目不是很赶,来了解一下,共勉。
JNI:Java Native Interface(Java 本地接口),它是为了方便Java调用C、C++等本地代码所封装的一层接口。
NDK:Native Development Kit(本地开发工具包),通过NDK可以在Android中更加方便的通过JNI来访问本地代码。
创建支持 C/C++ 的新项目
下载 NDK 和构建工具
只需要在创建的时候,在 Configure your new project 选项中,勾选 Include C++ Support 选项。其他的自己体会
将 C/C++ 代码添加到现有的项目中
如果你想将 native code 添加到一个现有的项目中,请按照下面的步骤操作:
创建新的 native source 文件,并将其添加到你的 Android Studio 项目中。如果你已经有了 native
code,也可以跳过这一步。创建一个 CMake 构建脚本。如果你已经有了一个 CMakeLists.txt 构建脚本,或者你想使用 ndk-build 然后有一个 Android.mk 构建脚本,也可以跳过这一步。
将你的 native library 与 Gradle 关联起来。Gradle 使用构建脚本将源码导入到你的 Android Studio 项目中,并且将你的 native library (也就是 .so 文件)打包到 APK 中。
一旦你配置好了项目,你就可以在 Java 代码中,使用 JNI 框架开调用原生函数(native functions)。只需要点击 Run 按钮,就可以编译运行你的 APP 了。
创建新的 native source 文件
请按照下面的方法来创建一个 cpp/ 目录和源文件(native source files):
- 打开IDE左边的 Project 面板,选择 Project 视图。
- 找到你项目的 module > src 目录,右键点击 main 目录,选择 New > Directory。
- 输入目录的名字(比如 cpp),然后点击 OK。
- 右键点击刚才创建好的目录,选择 New > C/C++ Source File。
- 输入文件名,比如 native-lib。
- 在 Type 菜单下拉选项中,选择源文件的扩展后缀名,比如 .cpp。
- 如果你也想创建一个头文件,点击 Create an associated header 选项框。
- 点击 OK。
创建 CMake 构建脚本
现在,你可以添加 CMake 命令来配置你的构建脚本了。为了让 CMake 将源代码(native source code)编译成 native library。需要在编译文件中添加 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 )
#然而,为了让 CMake 在编译时期能定位到你的头文件,你需要在 CMake 构建脚本中添加 include_directories()
# Specifies a path to native header files.
include_directories(src/main/cpp/include/)
生成的.so文件为 lib[name].so,在加载到java中的时候,只需要name就可以了,如下。
static {
System.loadLibrary(“native-lib”);
}
注意:如果你将 CMake 脚本里面的 library 重命名了,或者移除了。你需要清理一下你的工程。在 IDE 的菜单栏中选择 Build > Clean Project。
添加 NDK APIs
Android NDK 提供了一些有用的 native APIs。将 NDK librarys 添加到 CMakeLists.txt 脚本文件中,就可以使用这些 API 了。
- NDK中的libary有一部分预编译过了
预编译的 NDK librarys 已经存在在 Android 平台中了,所以你不需要编译它们,或者是将其打包到你的 APK 中。因为这些 NDK librarys 已经是 CMake 搜索路径的一部分,你甚至不需要提供你本地安装的 NDK 路径。你只需要向 CMake 提供你想使用的 library 名字。
将 find_library() 命令添加到你的 CMake 构建脚本中,这样就可以定位 NDK library 的位置,并将其位置存储在一个变量之中。你可以在构建脚本的其他地方使用这个变量,来代指 NDK library。下面的示例代码将 Android-specific log support library 的位置存储到变量 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 )
- NDK 同样也包含一些只包含源码的 library
//下面的命令告诉 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 )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.
# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )
添加其他的预编译库
添加预编译库和添加本地库(native library)类似。由于预编译库是已经构建好的,你想就要使用 IMPORTED 标志去告诉 CMake ,你只需要将其导入到你的项目中即可:
add_library( imported-lib
SHARED
IMPORTED )
然后你需要使用 set_target_properties() 命令去指定库的路径,就像下面的代码那样。
一些库会根据不同的 CPU 使用不同的包,或者是 Application Binary Interfaces(ABI),并且将他们归类到不同的目录中。这样做的好处是,可以充分发挥特定的 CPU 架构。你可以使用 ANDROID_ABI 路径变量,将多个 ABI 版本的库添加到你的 CMake 构建脚本中。这个变量使用了一些 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/ )
在 CMake 构建脚本中使用 target_link_libraries() 命令,将预构建库与你本地库相关联:
target_link_libraries( native-lib imported-lib app-glue ${log-lib} )
当你构建你的 APP 的时候,Gradle 会自动将导入的库打包到你的 APK 中。你可以使用 APK Analyzer 来检查。
关联本地库与 Gradle
右键点击你想链接本地库的 module,比如 app module,然后从菜单中选择 Link C++ Project with Gradle
手动配置 Gradle
如果要手动将 Gradle 与你的本地库相关联,你需要在 module 层级的 build.gradle 文件中添加 externalNativeBuild {} 代码块,并且在该代码块中配置 cmake {} 或 ndkBuild {}:
指定 ABI
一般情况下,Gradle 会将你的本地库构建成 .so 文件,然后将其打包到你的 APK 中。如果你想 Gradle 构建并打包某个特定的 ABI 。你可以在你的 module 层级的 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 {...}
}
如果你想控制 Gradle 构建、依赖你希望的东西,你就需要在 defaultConfig.externalNativeBuild.cmake {} 代码块或 defaultConfig.externalNativeBuild.ndkBuild {} 代码块中,配置其他的 abiFilters 标签。Gradle 会构建这些 ABI 配置,但是只会将 defaultConfig.ndk {} 代码块中指定的东西打包到 APk 中。