NDK与JNI就不多概述了,我所理解的NDK就是android开发中能够编译C/C++程序,构建SO,而JNI则是java提供的能够调用c/c++程序的接口,NDK与JNI开发也会分多篇讲解,大概会分为环境配置、JNI知识、优化三部分,其中JNI部分为android NDK开发的主要部分,主要涉及java与C/C++交互。

那么本篇涉及知识点如下,主要为配置部分:

1.android studio中配置NDK开发环境

2.CMake配置

android studio中配置NDK开发环境

android studio 2.2以上版本才会支持CMake,低版本只能使用ndk-build方式开发。

1.新建项目,勾选 Include C++ support

image.png

一路创建好项目,android会自动创建一个基于cmake的NDK项目.与普通项目不同的是:

在main文件夹下多了一个cpp文件夹,包含了一个native-lib.cpp文件

在APP目录下多了一个CMakeLists.txt文件夹

local.properties文件里会多了一个ndk.dir路径的配置

app目录下的build.gradle,会多了两处cmake的配置

externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}

直接程序运行会在屏幕上显示"Hello from C++".到此,说明一个NDK项目以及创建完成,很简单,这也是我推荐使用cmake的原因。

第一次创建,可能会出现很多环境问题,比如NDK下载不成功,运行不起来等等,需要你根据

错误查找具体的原因来解决。

2.现有项目 支持NDK配置

对于刚接触NDK配置的,其实很简单,同上面的步骤新创建一个支持NDK的项目,与旧项目结构对比不同之处,修改调整即可。你不创建新项目也可以,老司机可以直接开车,总结一下,大致为一下几点:

1.NDK下载,有NDK环境的就不用下了,下载安装下图红框的即可。

image.png

2.下载完毕,在local.properties配置你的NDK目录

ndk.dir=...

sdk.dir=...

3.在main目录下 新建一个cpp目录,用于存放cpp文件

4.在app目录下创建CMakeLists.txt文件,里面的配置内容是重点,放在本篇后面单独讲,并在app目录的build.gradle指定这个文件的路径并指定CMake配置,当然在Module中也可以,如下

defaultConfig中:

externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"//指定CMakeLists.txt配置
}
}

android中:

externalNativeBuild {
cmake {
path "CMakeLists.txt" //指定CMakeLists.txt路径
}
}

几个核心配置点,就是这么多。但是现在还运行不起来程序,把CMakeLists.txt配置完,写个JNI便可以了,下面我们就来看看CMake配置。

CMake配置

主要就是配置CMakeLists.txt,CMakeLists.txt就是编译so库的脚本文件,类似于原来的Android.mk,Application.mk。先看一下新建的NDK项目中CMakeLists.txt的内容:

#设置CMake插件的版本
cmake_minimum_required(VERSION 3.4.1)
#搜索指定的预构建库,并将该路径存储为一个变量
find_library( log-lib
log )
#定义要编译的源代码和最终要生成的库文件名称及类型
add_library(
native-lib #编译生成的库的名称,注意最终生成时会在前面加`lib`.
SHARED #生成的库的类型,有SHARED,STATIC,MODULE
src/main/cpp/native-lib.cpp ) #要编译的源代码文件
#如果有源码文件有头文件,指定头文件的目录
include_directories(src/main/cpp/include/)
#指定库的库应该链接到你的目标库。您可以链接多个库
target_link_libraries(
native-lib
${log-lib} )
以上是最基本的一个配置。
添加第三方的so库
因为已经编译好了,所以使用IMPORTED关键字
#设置so资源路径
set(my_lib_path ${CMAKE_SOURCE_DIR}/libs)
# 添加三方的so库
add_library(libfmod
SHARED
IMPORTED )
# 指名第三方库的绝对路径
set_target_properties( libfmod
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/${ANDROID_ABI}/libfmod.so )

当第三方库有针对不同架构编译了不同的库版本时,

有时候我们只需要引入我们想要的版本的库,当我们想要引入多个版本的库时,

可以使用ANDROID_ABI变量,它表示默认的ABI架构和NDK支持的架构,

如果我们在build.gradle中设置了过滤值,则表示过滤后的架构集合。

target_link_libraries(
libfmod
${log-lib} )

本地方法编写

新建类Test,加载so,并写一个本地方法helloWord,如下:

public class Test {
static {
#这里的native-lib就是前面CMakeLists.txt中添加的库名字
System.loadLibrary("native-lib");
#如果有三方so,也一并引入,注意不要lib
System.loadLibrary("第三方so");
}
#本地方法
public native String helloWord();
}

对应的C/C++方法为:

extern "C"
JNIEXPORT jstring JNICALL
Java_cn_mmdet_jean_Test_helloWord(JNIEnv *env, jobject instance) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

调用Test的helloWord就能看到C返回的内容了。

也许我讲的不够详细,但是配置这一块,我相信是最简单的,结合本篇文章以及相关资料,肯定就能搞定,最简单的方法还是使用android studio直接新建一个支持C++的项目,运行一下看看结果。然后主要看一下CMakeLists.txt的配置,配置部分就结束了。相比之前的.mk简单很多。