android studio从2.2版本开始支持CMake的方式来搭建NDK项目,在这个版本下NDK的开发流程大概分为环境配置、本地方法与头文件生成、C/C++程序编写、本地方法调用这几个步骤。
环境配置
1.NDK下载
在Android Studio界面,选择File→settings,然后找到android SDK→SDK Tools,在这个页面下可以下载NDK。
要为应用编译和调试原生代码需要以下组件:
1.Android 原生开发工具包 (NDK):这套工具集允许您为 Android 使用 C 和 C++ 代码,并提供众多平台库,让您可以管理原生 Activity 和访问物理设备组件,例如传感器和触摸输入。
2.CMake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。
3.LLDB:一种调试程序,Android Studio 使用它来调试原生代码。
这里把上述三个都下载。
2.Android Studio创建工程时自动生成C/C++项目目录
在创建项目时,在New Project界面中,我们要把Include C++ support选中,然后Next,直到Customize C++ Support界面。
我们可以使用下列选项自定义项目:
1.C++ Standard:使用下拉列表选择您希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置,这里我们选择C++ 11。
2.Exceptions Support:如果您希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake,我们选中。
3.Runtime Type Information Support:如果您希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake,我们选中。
然后我们点Finish,创建好的项目目录如下:
我们可以看到,比以前创建的项目多出两个目录,其中cpp目录存放的是C/C++的头文件如native-lib.cpp,External Build Files用于存放CMake脚本文件。
gradle多出了两个externalNativeBuile标签,第一个标签包含我们在创建项目选中的C++标准,异常支持和RTTI支持这三个标签。第二个标签包含cmake的脚本路径。
我们需要再gradle中的android{defaultConfig{}}中添加ndk支持的平台,比如:
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
}
}
ndk{
abiFilters "armeabi"
}
}
我们再来看看CMakeLists.txt:
原本项目生成的cmakeLists.txt有很多注释,上图为删减之后的结果。
本地方法与头文件生成
接下来我们要做的是创建本地方法和生成对应头文件。我们还是以最简单的HelloWorld为例,新建类文件HelloWorld:
public class HelloWorld {
static {
System.loadLibrary("HelloWorld");
}
public native String getHelloWorldFromJNI();
}
然后选择Build→Make Project,这时会在project视图下的app/build/intermediates/debug生成类编译文件。然后我们需要在命令行生成HelloWorld头文件。
在命令行里面定位到debug目录下:
cd app/build/intermediates/classes/debug
然后用javah编译类文件:
javah -jni com.example.caixunwei.ndktest.HelloWorld
这里有两点需要注意:
一是:如果在HelloWorld.java中import了其他类,则需要-classpath <path>参数设定加载类的路径,比如说我现在的项目应该设置为
set classpath=C:\android\workspace\NDKTest\app\src\main\java
然后再生成.h文件
javah -jni com.example.caixunwei.ndktest.HelloWorld
否则,会提示无法加载类之类的错误
二是:如果提示没有找到javah之类的,需要把jdk的bin地址添加到环境路径path中。
接着在当前debug目录下会生成头文件,生成的头文件com_example_caixunwei_ndktest_HelloWorld.h如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_caixunwei_ndktest_HelloWorld */
#ifndef _Included_com_example_caixunwei_ndktest_HelloWorld
#define _Included_com_example_caixunwei_ndktest_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_caixunwei_ndktest_HelloWorld
* Method: getHelloWorldFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_caixunwei_ndktest_HelloWorld_getHelloWorldFromJNI (JNIEnv , jobject )
#ifdef __cplusplus
}
#endif
#endif
如头文件第一段代码所说,我们不要编辑这个文件,而应该新建一个HelloWorld.cpp文件,把com_example_caixunwei_ndktest_HelloWorld.h的代码复制到HelloWorld.cpp文件里面,然后在HelloWorld.cpp中编写代码。
在cpp目录下新建HelloWorld.cpp文件,这时Android Studio会提示this is not a part of project,我们还需要在CMake脚本文件中将要新建的HelloWorld.cpp相关信息添加进去。
在CMakeLists.txt中添加:
add_library(HelloWorld
SHARED
src/main/cpp/HelloWorld.cpp )
target_link_libraries(HelloWorld
${log-lib} )
形式跟自动生成的十分相似,需要其他连接库还可以继续在target_link_libraries里面添加,然后点击Sync Now。此时在Android视图下cpp目录会自动生成HelloWorld的库目录。
然后将com_example_caixunwei_ndktest_HelloWord.h的代码复制进来,现在我们就可以编写逻辑代码了。
C/C++代码编写
JNIEXPORT jstring JNICALL Java_com_example_caixunwei_ndktest_HelloWorld_getHelloWorldFromJNI
(JNIEnv *env, jobject obj){
return env->NewStringUTF("HelloWorld!");
};
这里的逻辑代码很简单,只是在JNI环境下创建了一个字符串“HelloWorld!”然后返回。把生成的头文件的代码复制过来之后记得在命令行输入exit结束占用。
本地方法调用
调用该方法的结果如下:
完整的MainActivity的代码:
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
text = (TextView)findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
text.setText(new HelloWorld().getHelloWorldFromJNI());
}
});
}
}