多语言编程之 C and Java: Android NDK
目录
NDK(Native Debelopment Kit) 是 Android 提供的一个工具集,它允许在 Android app 使用 native 代码(C/CPP). 它集成了交叉编译器, 使用 mk 文件对平台、CPU 等进行隔离。在不同的平台下,只需要配置相应的 mk 文件即可以编译相就的模块。
1 NDK project 基本结构
2 Android.mk
Android.mk 文件存在于工程路径中的 jni/ 路径下。它可以看作是 GNUMakefile 的一部分,用来向编译器描述如何从 native 源码构建共享库(动态库)。
Android.mk 可以将源码分成不同的模块。这些 模块 可以是静态库、共享库(甚至是可执行文件)。静态库用来生成共享库,共享库则会和 app 打包。
2.1 基础
- LOCAL_PATH Android.mk 文件必须以此变量开始:
LOCAL_PATH := $(call my-dir)
该变量表明源文件的位置。此处的 my-dir 由编译器提供,返回当前路径(Android.mk所在的路径)
- CLEAR_VARS 该变量一般在第二行声明。此变量仍由编译器提供
include $(CLEAR_VARS)
该变量指向一个特殊的 GNUMakefile ,用来清理除了 LOCAL_PATH 以外的 LOCAL_xxx 变量,如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等。
- LOCAL_MODULE 变量:
LOCAL_MODULE := <module-name>
该变量用于定义将要编译的模块名称。该名称必须是唯一的。编译器会以此命名最终生成的模块文件,如:该变量为 gdal, 则生成的共享库名为 libgdal.so 。值得注意的是,不需要为模块名添加 lib 前缀,编译器会自动添加 lib 前缀和 so 后缀。编译器会自动检测模块名是否已经添加了前缀,如果已添加,则编译器在生成模块文件时不会再次添加。
- LOCAL_SRC_FILE 变量用于指定要编译的源文件。多个文件间用空格区分
LOCAL_SRC_FILE := sourcefile.c sfile.cpp
另外也可以使用 LOCAL_CPP_EXTENSION 来定义不同的 c++ 扩展名,其值以点符号开始,如 .hpp 。
- BUILD_SHARED_LIBRARY 或 BUILD_STATIC_LIBRARY
include $(BUILD_SHARED_LIBRARY)
该变量指向一个 GNUmakefile ,负责收集在 Android.mk 中从 'include $(CLEAR_VARS)' 到该变量之间所有的 LOCAL_XXX 的变量值。它将最终确定生成模块是什么,并确定如何生成它。
2.2 宏与变量
编译器为 Android.mk 提供了很多变量,大部分变量都已经有预分配的值,当然你也可以重新指定值 。
你也可以定义自己的变量。当你使用自定义变量名时,需要注意, NDK 编译器保留了如下变量名:
- 以 LOCAL_ 开头的变量名。如 LOCAL_MODULE
- 以 PROVATE_, NDK_ 或 APP 开头的变量名。编译器内部会有使用
- 小写的变量名。如 my_dir.依旧会在编译器内部使用
2.3 NDK 定义的变量
- CLEAR_VARS 见前文
- BUILD_SHARED_LIBRARY / BUILD_STATIC_LIBRARY 见前文
- PREBUILT_SHARED_LIBRARY / PREBUILT_STATIC_LIBRARY 指向一个预编译库的编译脚本。其 LOCAL_SRC_FILE 的值必须指向一个动态库或静态库
- TARGET_ARCH Android 指定的目标 CPU 架构的名称。如果是 ARM 兼容 CPU, 则名称为 arm .其它平台参见 TARGET_ARCH_ABI
- TARGET_PLATFORM 指定的 Android API 版本。例如, Android 5.1 系统镜像对应的 API 版本为 22
TARGET_PLATFORM := android-22
- TARGET_ARCH_ABI 目标 CPU + ABI 的名称。可以有一个或多个值。
TARGET_ARCH_ABI := arm64-v8a
参见下表:
CPU 平台和架构 | 变量值 |
---|---|
ARMv5TE | armeabi |
ARMv7 | armeabi-v7a |
ARMv8 AArch64 | arm64-v8a |
i686 | x89 |
x86-64 | x86-64 |
mips32 | mips |
mips64 | mips64 |
All | all |
- TARGET_ABI Android API 版本与 ABI 联合的名称。如
TARGET_ABI := android-22-ar,64-v8a
2.4 DNK 定义的方法宏
- my-dir 见前文
- all-subdir-makefiles 返回 my-dir 下所有包含 Android.mk 的子目录
- this-makefile 返回当前 makefile 所在的目录
- parent-makefile 返回上一层 makefile 所在目录
- grand-parent-makefile 返回上两层 makefile 所在的目录
- import-module 通过模块名找到另一个模块的 Android.mk 文件所在的目录.一个典型的用例如下:
$(call import-modules,<name>)
- 这将在环境参数NDK_MODULE_PATH所定义的目录下通过模块名<name>寻找Android.mk所在的目录
2.5 模块描述变量
- LCOAL_PATH 见前文
- LOCAL_MODULE 见前文
- LOCAL_MODULE_FILENAME 可以重新定义生成文件的文件名。默认情况下会生成 lib<LOCAL_MODULE>.so ,但可以通过此变量自定义文件名.(不需要定义扩展名)
- LOCAL_SRC_FILES 见前文
- LOCAL_CPP_EXTENSION 见前文
- LOCAL_CPP_FEATURES 可选变量,指定 C++ 代码所依赖指定的 C++ 特性。例如:
LOCAL_CPP_FEATURES := rtti exceptions
- LOCAL_C_INCLUDES 可选变量,指定头文件所有路径
- LOCAL_CFLAGS
- LOCAL_CPPFLAGS
- LOCAL_STATIC_LIBRARIES 编译所依赖的共享库
- LOCAL_SHARED_LIBRARIES 编译所依赖的静态库
- LOCAL_WHOLE_STATIC_LIBRARIES
- LOCAL_LDLIBS 用于额外的链接选项。
- LOCAL_LDFLAGS
- LOCAL_ALLOW_UNDEFINED_SYMBOLS 允许使用未定义的变量
- LOCAL_ARM_MODE 默认情况下,ARM目标二进制文件将会以 thumb 模式生成(16位指令),你可以将此变量设置为 arm,生成目标文件时将以 arm 模式生成(32位指令)。
- LOCAL_ARM_NEON 此变量设置为"true"的话,将允许你在C/C++中使用ARM 高级 SIMD (a.k.a. NEON) GCC intrinsics,就像汇编文件中的NEON指令.只有在使用'armeabi-v7a' ABI时使用此变量,注意不是所有的 基于armv7的CPU支持NEON指令集
- LOCAL_DISABLE_NO_EXECUTE Android NDK r4添加了支持"NX bit"的安全特性。在默认情况下此特性被激活,如果你不需要它,那么将此变量设置为"true"即可.
- LOCAL_DISABLE_RELRO
- LOCAL_DISABLE_FORMAT_STRING_CHECKS
- LOCAL_EXPORT_CFLAGS C/C++编译选项
- LOCAL_EXPORT_CPPFLAGS C++ 编译选项
- LOCAL_EXPORT_C_INCLUDES 与LOCAL_EXPORT_CFLAGS相同,只不过用于C的包含路径.
- LOCAL_EXPORT_LDFLAGS
- LOCAL_EXPORT_LDLIBS 与LOCAL_EXPORT_CFLAGS相同,只不过用于链接选项.注:被导入的链接选项将会加入在变量LOCAL_LDLIBS定义的选项之前.
3 Applocation.mk
3.1 概述
Application.mk 用于描述 native 模块(共享库、静态库或可执行文件)。它作为 GNUMakefile 的一部分,通常放在工程路径下的 jni 文件夹中。
3.2 变量
- APP_PROJECT_PATH 存储工程的绝对路径。当 Application.mk 文件在 jni 目录下时,此变量是可选的。
- APP_OPTIM 此变量指定编译优化方式 ,值可以是 release 或 debug.缺省值为 release.
- APP_CFLAGS 该变量用于存储 C 编译器的选项。它用在此处可以替换在 Android.mk 中定义的选项。如果为添加选项,则可以使用 += 添加来添加定义。
- APP_CPPFLAGS 同上。用于 C++
- APP_LDFLAGS 同上。
- APP_BUILD_SCRIPT 缺省值指向 jni 目录下的 Android.mk 文件
- APP_ABI 参见 TARGET_ARCH_ABI
- APP_PLATFORM 参见 TARGET_PLATFORM
- APP_STL 默认发问下,NDK 编译器提供了最小化的 C++ runtime library(system/lib/libstdc++.so),包括头文件。使用此变量可以指定其他 runtime:
Name Explanation Features libstdc++ (default) The default minimal system C++ runtime library. N/A gabi++_static The GAbi++ runtime (static). C++ Exceptions and RTTI gabi++_shared The GAbi++ runtime (shared). C++ Exceptions and RTTI stlport_static The STLport runtime (static). C++ Exceptions and RTTI; Standard Library stlport_shared The STLport runtime (shared). C++ Exceptions and RTTI; Standard Library gnustl_static The GNU STL (static). C++ Exceptions and RTTI; Standard Library gnustl_shared The GNU STL (shared). C++ Exceptions and RTTI; Standard Library c++_static The LLVM libc++ runtime (static). C++ Exceptions and RTTI; Standard Library c++_shared The LLVM libc++ runtime (shared). C++ Exceptions and RTTI; Standard Library - APP_SHORT_COMMANDS
- NDK_TOOLCHAIN_VERSION 指定 GCC 编译器的版本.
- APP_PIE 使用 PIE 来编译可执行文件(-fPIE).值可以为 true 或 false.
4 ndk-build
ndk-build 是 Android NDK 提供的一个脚本文件,它简化了调用 GNU makefile 的方法。我们在编译模块的时候,只需要调用 ndk-build就可以了:
cd <project>
$<ndk>/ndk-build
4.1 编译选项:
- clean 清理已经生成的编译文件
- V=1 打印编译命令
- -B 强制重新编译
- NDK_DEBUG NDK_DEBUG=0 编译为 release 版本; NDK_DEBUG=1 编译为 debug 版本
- NDK_HOST_32BIT =1 总是使用 32 位模式编译
- NDK_APPLICATION_MK=<file> 指定 Applocation.mk
- C <project> 指定工程路径。当命令行在工程路径下时,无需此选项。
5 附录
- Android NDK Downloads (需要梯子)
- android-doc