Android NDK 编译工具CMake的使用
Android Studio 用于构建原生库的默认工具是 CMake。由于很多现有项目都使用构建工具包编译其原生代码,Android Studio 还支持 ndk-build。不过,如果您在创建新的原生库,则应使用 CMake。
创建支持原生代码的项目与创建任何其他 Android Studio 项目类似,不过前者还需要额外几个步骤:
1. 创建新 Android 项目
创建新的 Android 项目,需要选中 Include C++ Support 复选框,即支持C++的。
2.自定义C++的配置
依次Next
后,Create Android project
> Target Android Devices
> Add an Activity to Mobile
> Configure Activity
> Customize C++ Support
为了方便分析,Exceptions Support、Runtime Type Information Support 我们也同时勾选上,不过一般情况下可以不勾选。
在 Customize C++ Support中,我们可以自定义一下内容:
- C++ Standard :使用下拉列表选择你希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
- Exceptions Support :如果你希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将
-fexceptions
标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。 - Runtime Type Information Support :如果你希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将
-frtti
标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
3.分析项目结构
先切换视图到Project
方便查看。
对比普通新建项目的内容,可以发现示例有4个地方发生了变化:
- 添加了
cpp
目录,里面有示例 C++ 源文件native-lib.cpp
,它的代码里面提供了一个简单的 C++ 函数 stringFromJNI(),此函数可以返回字符串“Hello from C++”。 MainActivity.java
中添加了两个部分,一个负责加载so库;一个则为native方法。build.gradle
中也添加了两个部分,一个为C++添加异常处理、RTTI的处理的支持;一个则为CMake封装构建配置,同时也提供一个相对路径给CMake的构建脚本CMakeLists.txt
;- 添加了
CMakeLists.txt
脚本文件,它的主要作用是定义了哪些文件需要编译以及和其他库的关系,即辅助Cmake进行系列的操作。
注:native相关方法去掉报红
取消检测即可,打开 Settings>Editor>Inspections>Android>Missing JNI function 去掉勾选。
新的项目完成创建后,我们可以清晰地看到结构及内容上的变化:
3.1 so库/native方法
MainActivity.java
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
下面说明一下相关的内容:
- System.loadLibrary(“native-lib”)负责加载动态的原生库文件,
native-lib
为CMakeLists.txt
中定义的library。
CMake 使用以下规范来为库文件命名:
格式:
lib库名称.so
注:如果你在构建脚本中指定“native-lib”作为共享库的名称,CMake 将创建一个名称为 libnative-lib.so
的文件。不过,在 Java 代码中加载此库时,请使用你在 CMake 构建脚本中指定的名称。
- stringFromJNI为native方法与
native-lib.cpp
的Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI的相对应的。
3.2 原生源文件
native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
其中Java_com_brainbg_nkdcmakeall_MainActivity_stringFromJNI
是根据MainActivity.java
的stringFromJNI()
生成的,其格式如下:
格式:Java_包名_类名_方法名
3.3 构建配置
这部分主要是关于Cmake、C++相关的构建配置。
build.gradle
android {
defaultConfig {
......
//Exceptions Support(-fexceptions)、Runtime Type Information (-frtti) 构建关联
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
}
//封装CMake构建配置
externalNativeBuild {
//提供一个相对路径给CMake构建脚本
cmake {
path "CMakeLists.txt"
}
}
}
对比ndk-build和CMake的配置
//CMake需要关联CMakeLists.txt
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
//ndk-build需要关联Android.mk
externalNativeBuild {
ndkBuild {
path file('jni/Android.mk')
}
}
3.4 脚本配置
CMakeLists.txt
# 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.
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 )
# 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.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
上面的配置简单说明下:
- cmake_minimum_required:CMake所需的最低版本要求,目前为3.4.1。
- add_library:
- Sets the name of the library:设置库的名称。例如指定
native-lib
作为共享库的名称,那么CMake 将创建一个名称为libnative-lib.so
的库文件。 - Sets the library as a shared library:将库设置为共享库.
- Provides a relative path to your source file(s):提供源文件的相对路径
- Sets the name of the library:设置库的名称。例如指定
- find_library:
- target_link_libraries:
更多关于Cmake的用法可以查看CMake文档
3.5 运行流程
下面为官方NDK文档的流程说明:
Gradle
调用您的外部构建脚本CMakeLists.txt
。CMake
按照构建脚本中的命令将 C++ 源文件native-lib.cpp
编译到共享的对象库中,并命名为libnative-lib.so
,Gradle 随后会将其打包到 APK 中。- 运行时,应用的
MainActivity
会使用System.loadLibrary()
加载原生库。现在,应用可以使用库的原生函数stringFromJNI()
。 MainActivity.onCreate()
调用stringFromJNI()
,这将返回“Hello from C++”并使用这些文字更新TextView
。
根据本人的理解,制作了一张简单流程图:
参考资料
https://github.com/googlesamples/android-ndk/tree/master/hello-libs
https://github.com/android-ndk/ndk
https://developer.android.google.cn/studio/projects/add-native-code.html#link-gradle
https://cmake.org/cmake/help/latest/index.html#
Author
作者:Brainbg(白雨)
GitHub:https://github.com/Brainbg
博客:https://www.brainbg.com/
CSDN:https://blog.csdn.net/u014720022
简书:https://www.jianshu.com/u/94518ede7100