Android ndk开发

什么是Android NDK

Android NDK 是一套允许您使用 C 和 C++ 等语言,以原生代码实现部分应用的工具集。在开发某些类型的应用时,这有助于您重复使用以这些语言编写的代码库。

Android NDK开发流程

ndk下载

由于android studio自带的ndk版本可能在编译中出现问题,所以自己在官网下载所需的ndk版本。本文以稳定版本(r16b)为例子。

ndk环境配置

解压现在好的压缩文件到自己的目标目录,如我的目录为:C:\sdk\ndk-bundle\android-ndk-r16b,下一步配置环境变量,打开我的电脑–>鼠标右键选择属性–>高级系统设置–>环境变量–> 找到系统变量中的path变量进行编辑–>把NDK解压后文件目录主放到path最后面,记得加“;”。
下一步,进入cmd命令窗口,输入ndk-build指令,如下图则表示环境配置成功。Android ndk开发

对Android Studio进行ndk配置

新建项目,打开File–Project Structure–SDK Location,在Android NDK Location中选择ndk的目录,并在gradle.properties的文件末尾加上android.useDeprecatedNdk=true,如下图所示:
Android ndk开发

ndk开发

###通过C/C++代码编译.so库(以人脸识别为例)
1、新建项目,勾选Include C++ support选项
Android ndk开发
2、创建一个类FaceDetect,实现接口中的函数,如图:
Android ndk开发
3、点击android studio下方的Terminal,首先cd到java目录,然后执行指令javah -jni com.example.facetest.FaceDetect,若该类中有中文便会报以下错误,删掉中文即可,ndk编译成功后便可在java目录下见到com_example_facetest_FaceDetect.h头文件,将该文件剪切到cpp文件夹中
Android ndk开发
4、在cpp文件夹中创建.c文件并实现头文件中的方法,并引入生成的头文件,并将需要引入的文件复制到cpp文件夹中,如图所示:
Android ndk开发
5、配置CMakeList.txt文件,其中声明引入文件的路径和生成库的名称和资源文件等,详细例子见下方代码。(该示例中引用了OpenCV中的库和ncnn库,所以需要提前配置好支持android开发的OpenCV环境,这一块可以百度一下)

cmake_minimum_required(VERSION 3.4.1)

#include头文件目录
include_directories(src/main/cpp/opencv2
                    src/main/cpp/include
                    src/main/cpp)

#source directory源文件目录
file(GLOB FACE_DETECT_SRC src/main/cpp/*.h
                   src/main/cpp/*.cpp)


set(FACE_DETECT_COMPILE_CODE ${FACE_DETECT_SRC})

#添加ncnn库
add_library(libncnn STATIC IMPORTED )
set_target_properties(libncnn
  PROPERTIES IMPORTED_LOCATION
  ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libncnn.a)

#添加opencv中的库
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3
    PROPERTIES IMPORTED_LOCATION
    ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

add_library(libopencv_core STATIC IMPORTED )
set_target_properties(libopencv_core PROPERTIES
    IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_core.a)

add_library(libopencv_highgui STATIC IMPORTED )
set_target_properties(libopencv_highgui PROPERTIES
    IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_highgui.a)

add_library(libopencv_imgcodecs STATIC IMPORTED )
set_target_properties(libopencv_imgcodecs PROPERTIES
    IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_imgcodecs.a)

add_library(libopencv_imgproc STATIC IMPORTED )
set_target_properties(libopencv_imgproc PROPERTIES
    IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_imgproc.a)

add_library(libopencv_objdetect STATIC IMPORTED )
set_target_properties(libopencv_objdetect PROPERTIES
    IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_objdetect.a)

#编译为动态库
add_library(Face_Detect SHARED ${FACE_DETECT_COMPILE_CODE})

#添加工程所依赖的库
find_library(  log-lib log )
target_link_libraries( Face_Detect
                       libncnn
                       libopencv_java3
                       libopencv_core
                       libopencv_highgui
                       libopencv_imgcodecs
                       libopencv_imgproc
                       libopencv_objdetect
                       ${log-lib})

6、在app下的build.gradle进行如下配置,在defaultConfig板块内加入以下代码:

ndk{
	moduleName "Face_Detect"
	//设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so)
    abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
    stl "gnustl_static"
}

其中moduleName值得是生成的库的名称,与创建的FaceDetect类中引用的库一致。
至此,点击运行即可编译成功,进入app–build–intermediates–cmake–debug–obj–armeabi-v7a目录便可见到新生成的库libFace_Detect.so。

引用编译好的库进行开发

1、新建项目,在Ndk环境已经配置好的前提下,不需要勾选Include C++ support,除非你要在此基础上再编译一个库。
2、将库导入到该项目中,新建jniLibs文件夹,将包含该库如:libFace.so的armeabi-v7a的文件夹复制到jniLibs文件夹中,并在app下的build.gradle添加以下代码,如图(以armeabi-v7a架构为例):
Android ndk开发
3、创建该接口的类,注意:类的包名和类名需要和编译的库一致,不然会报错。看图,原因如下:
Android ndk开发
编译库时生成的.h头文件中指定了包名(横线部分)和类名(圆圈),该头文件也被引入C++文件中进行编译成库,所以需要使其一致,所以项目目录结构如下:
Android ndk开发
至此便可成功调用库中的函数。

总结

当编写代码出现bug时,要有一套debu在这里插入代码片g的策略,我的主要流程是:
1、查看LogCat找出bug定位与类型;
2、尝试自己解决(对自己的代码进行review,进行断点调试,Log输出重要数据的内容等);
3、能力之外的进行百度与google;
4、查看相关官方文档与源码(极其重要),因为许多开发都是围绕源码进行的,这是根本,可以少走很多弯路;
大家如果有更好的办法的话,提出来一起参考,一起进步。拜谢!