~Android ndk及jni技术
Android NDK使用入门
概念
原生开发套件(NDK)用于Android应用中使用C/C++代码。
Java原生接口(JNI)是Java与C++相互通信的接口。
应用二进制接口(ABI)对应不同的处理器架构。
ndk构建系统
主要有三种:
- 基于make的ndk-build
- CMake
- 独立工具链
ndk-build
ndk-build脚本基于make构建项目,其相当于命令:
$GNUMAKE -f <ndk>/build/core/build-local.mk <parameters>
使用ndk-build就像使用make一样,其有额外选项:
clean
移除之前生成的所有二进制文件V=1
启动构建,并显示构建命令-B
强制执行完整的重新构建NDK_LOG=1
显示内部NDK日志消息(用于调试NDK本身)NDK_DEBUG=1
强制执行可调试buildNDK_DEBUG=0
强制执行发布buildNDK_HOST_32BIT=1
始终使用32位模式下的工具链NDK_APPLICATION_MK=file
特定Application.mk文件进行构建-C project
构建位于project的项目路径的原生代码(和make一样)
Android.mk
Android.mk文件位于项目的jni子目录中,用于向构建系统描述源文件和共享库,实际上是一个Makefile片段。
用于定义Application.mk和构建系统所未定义的项目级设置。
基础知识
Android.mk文件必须先定义LOCAL_PATH变量:
LOCAL_PATH := $(call my-dir)
表示源文件的位置,宏函数my-dir返回Android.mk文件所在目录。
然後,清除LOCAL_XXX变量:
include $(CLEAR_VARS)
CLEAR_VARS变量指向一个Makefile文件。
使用LOCAL_MODULE变量存储要构建的模块名称:
LOCAL_MODULE := hello-jni
模块名称必须唯一且不含空格,构建系统会自动为之增加正确的前缀(lib)和后缀(.so)。
增加源文件,即给LOCAL_SRC_FILES赋值或追加文件名,例如:
LOCAL_SRC_FILES := hello-jni.c
最后,将构建系统包含进去:
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY变量指向Makefile文件确定了构建的内容及方式。
变量和宏
NDK构建系统保留以下名称(避免使用):
- 以
"LOCAL_"开头 - 以
"PRIVATE_","NDK_"和以"APP"开头 - 小写名称
用于include的变量
CLEAR_VARS
用于取消"开发者定义的变量"中列出的几乎所有LOCAL_XXX变量BUILD_EXECUTABLE
构建可执行文件BUILD_SHARED_LIBRARY
构建共享库BUILD_STATIC_LIBRARY
构建静态库PREBUILT_SHARED_LIBRARY
预构建共享库PREBUILT_STATIC_LIBRARY
预构建静态库
目标信息变量
TARGET_ARCH
CPU系列:arm,arm64,x86或x86_64TARGET_PLATFORM
Android API级别号TARGET_ARCH_ABI
目标ABITARGET_ABI
目标Android API级别与ABI的串联
模块描述变量
LOCAL_PATH
指定当前文件的目录路径LOCAL_MODULE
用于存储模块名称LOCAL_MODULE_FILENAME
生成的模块默认使用的完整名称LOCAL_SRC_FILES
所用的源文件列表LOCAL_CPP_EXTENSION
为C++源文件指定cpp以外的扩展名LOCAL_CPP_FEATURES
指定C++代码依赖的特定功能LOCAL_C_INCLUDES
指定相对于NDK根目录的包含路径(或者使用绝对路径)LOCAL_CFLAGS
构建C/C++传递的编译器标记LOCAL_CPPFLAGS
只构建C++传递的编译器标记LOCAL_STATIC_LIBRARIES
存储当前模块依赖的静态库列表LOCAL_SHARED_LIBRARIES
存储当前模块依赖的动态库列表LOCAL_WHOLE_STATIC_LIBRARIES
存储当前模块依赖的静态库列表(视为完整归档)LOCAL_LDLIBS
指定额外链接库文件的参数LOCAL_LDFLAGS
指定其他链接器参数(用于动态库)LOCAL_ALLOW_UNDEFINED_SYMBOLS
是否允许未定义引用(用于动态库)LOCAL_ARM_MODE
指定构建模式为arm,默认构建模式thumb.
可以源文件加上".arm"后缀以特定arm构建LOCAL_ARM_NEON
是否启用NEON指令,需检测CPU支持LOCAL_DISABLE_FORMAT_STRING_CHECKS
是否停用格式字符串检查(printf使用的应为常量)LOCAL_EXPORT_CFLAGS
记录导出标志,使用LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES的模块(构建不使用导出标志)可以添加此参数,依赖于这些模块的目标(构建使用导出标志)的LOCAL_CFLAGS在构建时会继承到此参数LOCAL_EXPORT_CPPFLAGS
与LOCAL_EXPORT_CFLAGS相同,但仅用于C++标记LOCAL_EXPORT_C_INCLUDES
与LOCAL_EXPORT_CFLAGS相同,但适用于C include路径LOCAL_EXPORT_LDFLAGS
与LOCAL_EXPORT_CFLAGS`相同,但适用于链接器标记LOCAL_EXPORT_LDLIBS
与LOCAL_EXPORT_CFLAGS`相同,但适用于链接其他库文件LOCAL_SHORT_COMMANDS
是否使用短命令行(win的命令行最多只接受8191个字符)LOCAL_THIN_ARCHIVE
构建静态库时,是否生成瘦归档LOCAL_FILTER_ASM
过滤汇编文件的shell命令(接受输入输出汇编文件名这两个参数),构建系统将生成临时汇编文件然後执行此命令,再将过滤後的汇编文件编译到对象文件中
NDK提供的函数宏
- my-dir
返回当前Android.mk的目录,实际上返回make包含的最后一个Makefile的目录 - all-subdir-makefiles
返回当前my-dir路径所有子目录中的Android.mk文件列表 - this-makefile
返回当前Makefile的路径 - parent-makefile
返回父Makefile路径 - grand-parent-makefile
返回祖父Makefile路径 - import-module
返回查找模块的Android.mk文件,格式如下:
$(call import-module,<name>)
Application.mk
同样位于jni子目录下,是一个Makefile文件,指定ndk-build的项目级设置。
其中,可定义变量有:
APP_ABI(可以指定多个值)指令集 值 32位ARMv7 armeabi-v7a 64位ARMv8(AArch64) arm64-v8a x86 x86 x86_64x86_64默认 all APP_ASFLAGS
传递给每个汇编源文件的编译器标记APP_ASMFLAGS
传递给每个YASM源文件的标记APP_BUILD_SCRIPT
默认加载jni/Android.mk,设置此值以加载其他Android.mk文件(绝对路径)APP_CFLAGS
项目C/C++编译传递的标记APP_CLANG_TIDY
是否启用clang-tidy,默认停用APP_CLANG_TIDY_FLAGS
为项目的clang-tidy传递的标记APP_CONLYFLAGS
项目C编译传递的标记APP_CPPFLAGS
项目C++编译传递的标记APP_CXXFLAGS
与APP_CPPFLAGS相同,但在编译命令中将出现在APP_CPPFLAGS之后APP_DEBUG
是否构建可调试的应用APP_LDFLAGS
链接可执行文件或动态库传递的标记(不影响静态库)APP_MANIFEST
AndroidManifest.xml文件的绝对路径APP_MODULES
要构建的模块的显式列表APP_OPTIM
将此可选变量定义为release或debugAPP_PLATFORM
此应用的Android API级别,如"android-16"APP_PROJECT_PATH
项目根目录的绝对路径APP_SHORT_COMMANDS
LOCAL_SHORT_COMMANDS的项目级等效项APP_STL
用于此应用的C++标准库APP_STRIP_MODE
为此应用中的模块而传递给strip的参数,默认--strip-unneededAPP_THIN_ARCHIVE
是否为项目中的所有静态库使用瘦归档APP_WRAP_SH
要包含在此应用中的wrap.sh文件的路径。<br> 每个ABI都有对应的变量:APP_WRAP_SHAPP_WRAP_SH_armeabi-v7aAPP_WRAP_SH_arm64-v8aAPP_WRAP_SH_x86APP_WRAP_SH_x86_64
使用预构建库
预构建库是之前编译生成的库文件,其使用需要遵循流程。
将预构建库作为模块
预构建库文件与其Android.mk文件位于同一目录,则可用直接用LOCAL_SRC_FILES变量(不包含源文件)来指定单一的预构建库(相对路径),(当然可以直接用绝对路径)。
然後,包含PREBUILT_SHARED_LIBRARY或PREBUILT_STATIC_LIBRARY变量的脚本,以定义包含此预构建库的模块(模块名可以与预构建库名不一样)。
补充:LOCAL_SRC_FILES可以配合TARGET_ARCH_ABI变量选择不同架构的预构建库文件,而预构建库的头文件可以使用LOCAL_EXPORT_C_INCLUDES变量导出(绝对路径)。
在其他模块中使用预构建库
在其他模块中使用,只需要将预构建库所属模块添加到LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES变量中。