android NDK开发

简介:

android NDK开发有通过外部的构建工具将原生代码构建成库文件,也有用开发工具自身带的,android studio支持CMake和ndk-build来构建原生库,这里我们介绍通过CMake来构建原生代码。

JNI和NDK

JNI全称是java native interface,即java原生代码接口,其作用就是连接底层代码c/c++和java之间的交互.
NDK全称是Native Development Kit,即原生开发的工具包,里面包含着快速开发的动态库,以及相关构建工具等

为什么使用JNI

  1. 支持跨平台,用c/c++编译的so库可以在其它平台上很好移植,复用度高
  2. 提高性能效率
  3. 代码安全性高,反编译难度大

下载安装工具

打开SDK Manager 安装android NDK,CMake,LLDB

  • android NDK:原生开发工具包
  • CMake :原生代码构建工具
  • LLDB :调试原生代码程序工具

创建项目

在向导中勾选include c++ support,并设置下面选项

  1. C++ Standard:使用下拉列表选择您希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
  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。

构建运行项目

  1. Gradle 调用您的外部构建脚本 CMakeLists.txt。
  2. CMake 按照构建脚本中的命令将 C++ 源文件 native-lib.cpp 编译到共享的对象库中,并命名为libnative-lib.so,Gradle 随后会将其打包到 APK 中。
  3. 运行时,应用的 MainActivity 会使用 System.loadLibrary() 加载原生库。现在,应用可以使用库的原生函数 stringFromJNI()。
  4. MainActivity.onCreate() 调用 stringFromJNI(),这将返回“Hello from C++”并使用这些文字更新 TextView。

CMakeLists.txt

CMakeLists.txt是构建脚本文件,这里我们看新创建的demo里的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cmake_minimum_required(VERSION 3.4.1)
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 )
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 )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )

这里我们通过相关cmake命令可知,这里是指定了cmake的版本,指定了库文件以及查找并关联了一个log的库文件

配置CMakeLists.txt文件路径

1
2
3
4
5
6
7
8
9
externalNativeBuild {
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "CMakeLists.txt"
}
}

配置ABI

1
2
3
4
5
6
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'
}

库命名

在cmake脚本文件里通过add_library命令中指定库文件名字,其产生文件格式为
lib库名称.so
代码中通过 System.loadLibrary(“库名称”);来进行库的加载

旧NDK版本支持

在gradle.properties中添加配置android.useDeprecatedNdk=true

ndk-build构建

除了cmake方式,我们还可以使用ndk-build方式构建,在编译文件目录下增加Android.mk文件和Application.mk文件
在Android.mk中配置编译相关配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
LOCAL_PATH := $(call my-dir)
// 设置工作目录,而my-dir则会返回Android.mk文件所在的目录
include $(CLEAR_VARS)
// 清除几乎所有以LOCAL——PATH开头的变量(不包括LOCAL_PATH)
LOCAL_MODULE := hello_jni
// 设置模块的名称,即编译出来.so文件名
// 注,要和上述步骤中build.gradle中NDK节点设置的名字相同
LOCAL_SRC_FILES := test.cpp
// 指定参与模块编译的C/C++源文件名
include $(PREBUILT_SHARED_LIBRARY)
// 生成预先构建共享库
LOCAL_LDLIBS := -ljnigraphics -llog
// 添加本地支持的动态库
LOCAL_SHARED_LIBRARIES := skia_android
//指定生成的静态库或者共享库在运行时依赖的共享库模块列表
include $(BUILD_SHARED_LIBRARY)
// 指定生成的共享库,静态库则为BUILD_STATIC_LIBRARY

在Application.mk文件中则配置项目相关信息

1
2
3
4
5
6
7
8
APP_PLATFORM := android-15
//指定支持的版本
APP_STL:=gnustl_static
//指定运行库
APP_ABI := armeabi
//指定库支持的架构
APP_CPPFLAGS := -frtti -std=c++11
//指定相关配置

在gradle中指定Android.mk构建文件的路径

1
2
3
4
5
externalNativeBuild {
ndkBuild {
path 'jni/Android.mk'
}
}

最后通过cd命令到编译文件目录下通常为jni目录下执行ndk-build命令构建库文件即可打包成so包

开发流程

  1. 在Java中声明Native方法(即需要调用的本地方法)
  2. javac编译上述 Java源文件得到 .class文件
  3. 通过 javah 命令导出JNI的头文件(.h文件)
  4. 使用 Java需要交互的本地代码 实现在 Java中声明的Native方法
  5. 编译.so库文件

注册JNI函数

有两种方式可以注册JNI函数,一种是静态注册,一种是动态注册,静态注册就像上面讲的通过javah得到对应的头文件,实现其函数即可,动态注册则是通过库加载调用的JNI_OnLoad()函数中调用JNIEnv的RegisterNatives方法来完成注册native方法和JNI函数的对应关系

c调用java代码

通过FindClass()函数获取到对应的java类,然后通过GetMethodID()函数获取到对应的方法