~Android ndk及jni技术

Android NDK使用入门

概念

原生开发套件(NDK)用于Android应用中使用C/C++代码。
Java原生接口(JNI)是Java与C++相互通信的接口。
应用二进制接口(ABI)对应不同的处理器架构。

原生activity和应用

JNI提示

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
    强制执行可调试build
  • NDK_DEBUG=0
    强制执行发布build
  • NDK_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_64
  • TARGET_PLATFORM
    Android API级别号
  • TARGET_ARCH_ABI
    目标ABI
  • TARGET_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_LIBRARIESLOCAL_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_64 x86_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或debug

  • APP_PLATFORM
    此应用的Android API级别,如"android-16"

  • APP_PROJECT_PATH
    项目根目录的绝对路径

  • APP_SHORT_COMMANDS
    LOCAL_SHORT_COMMANDS的项目级等效项

  • APP_STL
    用于此应用的C++标准库

  • APP_STRIP_MODE
    为此应用中的模块而传递给strip的参数,默认--strip-unneeded

  • APP_THIN_ARCHIVE
    是否为项目中的所有静态库使用瘦归档

  • APP_WRAP_SH
    要包含在此应用中的wrap.sh文件的路径。<br> 每个ABI都有对应的变量:

    • APP_WRAP_SH
    • APP_WRAP_SH_armeabi-v7a
    • APP_WRAP_SH_arm64-v8a
    • APP_WRAP_SH_x86
    • APP_WRAP_SH_x86_64

使用预构建库

预构建库是之前编译生成的库文件,其使用需要遵循流程。

将预构建库作为模块

预构建库文件与其Android.mk文件位于同一目录,则可用直接用LOCAL_SRC_FILES变量(不包含源文件)来指定单一的预构建库(相对路径),(当然可以直接用绝对路径)。

然後,包含PREBUILT_SHARED_LIBRARYPREBUILT_STATIC_LIBRARY变量的脚本,以定义包含此预构建库的模块(模块名可以与预构建库名不一样)。

补充:LOCAL_SRC_FILES可以配合TARGET_ARCH_ABI变量选择不同架构的预构建库文件,而预构建库的头文件可以使用LOCAL_EXPORT_C_INCLUDES变量导出(绝对路径)。

在其他模块中使用预构建库

在其他模块中使用,只需要将预构建库所属模块添加到LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES变量中。

CMake

CPU和C++支持

调试和性能分析

高性能音频

Vulkan

机器学习


~Android ndk及jni技术
https://blog.siantao.top/技术/计算机/软件/Android/Native/~Android ndk及jni技术/
作者
玉水仙楊
发布于
2022年4月1日
许可协议