NDK
Android NDK 是将C或C++(原生代码)嵌入到 Android 应用中的工具。
在 Android 应用中对想执行以下一项或多项操作的开发者特别有用:
- 在平台之间移植其应用
- 重复使用现有库
- 提高性能,特别是计算密集型应用。
- Android.mk,必须在 jni 文件夹内创建的配置文件,其中定义了模块及其名称、编译的源文件、版本标志以及需要链接的库。 ndk-build 将查看此文件,并编译。
- Application.mk:此文件枚举并描述应用需要的模块信息。 这些信息包括:
-
- 针对特定平台进行编译的ABI
- 工具链
- 要包含的标准库
Android.mk 文件位于 jni 目录,用于向构建系统描述源文件和共享库,与makefile文件类似。
Android.mk 由N个模块构成,每个模块类似于一个独立的makefile文件。
模块可以是静态库、共享库、独立可执行文件。
构建系统只会将共享库放入应用软件包。 此外,静态库可生成共享库。
NDK构建系统还可处理其他信息。如,无需在 Android.mk 中列出头文件或生成文件之间的显式依赖关系。 NDK会自动计算这些关系。
预留变量
- 以 LOCAL_ 开头的名称,如LOCAL_MODULE
- 以PRIVATE_,NDK_,APP开头的名称,构建系统在内部使用这些变量
- 小写的变量名,例如 my-dir,构建系统在内部也使用这些变量;
如果需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_
NDK定义的变量
CLEAR_VARS
CLEAR_VARS指向特殊的GNU Makefile文件,用于清除开发者定义的LOCAL_XXX 模块变量。如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等。
注意,它不会清除 LOCAL_PATH。 在描述每个模块之前,必须定义了此变量。
BUILD_SHARED_LIBRARY
收集距离上一个include $(CLEAR_VARS)中提供的信息,确定如何从列出的源文件构建目标共享库。
请注意,使用此变量前至少要为 LOCAL_MODULE 和 LOCAL_SRC_FILES赋值。
include $(BUILD_SHARED_LIBRARY)
- 1
生成名为lib$(LOCAL_MODULE).so的动态库文件
BUILD_STATIC_LIBRARY
与BUILD_SHARED_LIBRARY类似,不过建立的是静态库文件
include $(BUILD_STATIC_LIBRARY)
- 1
生成名为lib$(LOCAL_MODULE).a的静态库文件
PREBUILT_SHARED_LIBRARY
用于指向已编译好的so,LOCAL_SRC_FILES不在指向源文件, 而是指向共享库的路径,以libfoo.so为例。
include $(CLEAR_VARS) LOCAL_MODULE := foo-prebuilt LOCAL_SRC_FILES := libfoo.so include $(PREBUILT_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
example
必须为每个预构建库定义一个独立模块。为此,请执行以下步骤:
- 定义一个模块名称,此名称不需要与预构建库本身的名称相同;
- 在Android.mk 文件中,LOCAL_SRC_FILES等于预构建库路径, 路径为LOCAL_PATH的相对路径。
必须确保选择与目标ABI对应的预构建库版本。 如需了解有关确保库支持 ABI 的详细信息,请参阅为预构建库选择 ABI。
ABI,可认为系统架构不同,指令集不同
根据使用共享 (.so) 还是静态 (.a) 库,添加 PREBUILT_SHARED_LIBRARY 或 PREBUILT_STATIC_LIBRARY。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := foo-prebuilt LOCAL_SRC_FILES := libfoo.so include $(PREBUILT_SHARED_LIBRARY) # 引用与构建库 include $(CLEAR_VARS) LOCAL_MODULE := foo-user LOCAL_SRC_FILES := foo-user.c LOCAL_SHARED_LIBRARIES := foo-prebuilt # 预构建库模块, foo-user依赖foo-prebuilt,即libfoo.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include # 导出预购库依赖的头文件 include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
如果应用针对早于 Android 4.3(Android API 级别 18)的 Android 版本,并且使用共享库,则必须先加载共享库,再加载依赖它的其他库。
例如,应用可能具有以下模块:
- libfoo.so
- libfoo.so 使用的 libbar.so
- libfoo 和 libbar 使用的 libstlport_shared.so
必须以相反的依赖顺序加载库(即优先加载被依赖so):
static { System.loadLibrary("stlport_shared"); System.loadLibrary("bar"); System.loadLibrary("foo"); }
- 1
- 2
- 3
- 4
- 5
注:调用 System.loadLibrary() 时不要使用 lib 前缀
PREBUILT_STATIC_LIBRARY
与 PREBUILT_SHARED_LIBRARY 相同,但用于静态库。
模块自定义变量
每个模块描述应遵守以下基本流程:
- 在定义模块前,使用 CLEAR_VARS 变量初始化(取消定义)与模块相关的变量,相当于将变量赋值为NULL
- 给LOCAL_MODULE赋值
LOCAL_PATH
Android.mk 文件必须先定义 LOCAL_PATH 变量
LOCAL_PATH := $(call my-dir)
- 1
此变量表示Android.mk文件在计算机中的路径,构建系统的宏函数 my-dir 返回当前路径(包含 Android.mk 文件本身的目录)的路径。
CLEAR_VARS指向的脚本不会清除此变量
使用语法
include $(CLEAR_VARS)
- 1
LOCAL_MODULE
LOCAL_MODULE是模块名称,每个模块都必须定义此变量
LOCAL_MODULE := module_name
- 1
每个模块名称必须唯一,且不含任何空格。构建系统在生成最终共享库文件时,会将正确的前缀和后缀自动添加到模块名称。 例如,上例会生成一个名为libmodule_name.so的库。
注,如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。
LOCAL_MODULE_FILENAME
此可选变量覆盖构建系统默认用于其生成的文件的名称。 例如,如果 LOCAL_MODULE 的名称为 foo,可以强制系统将它生成的文件命名为 libnewfoo。 如:
LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo
- 1
- 2
对于共享库模块,此示例将生成一个名为 libnewfoo.so 的文件。
LOCAL_SRC_FILES
LOCAL_SRC_FILES为要构建的C和C++ 源文件列表,以空格分隔多个文件
LOCAL_CPP_EXTENSION
可以使用此可选变量为 C++ 源文件指定 .cpp 以外的文件扩展名
LOCAL_CPP_EXTENSION := .cxx
- 1
LOCAL_CPP_FEATURES
相当于本模块的编译选项。
可使用此可选变量指明代码在构建过程中使用的编译器和链接器标志。对于预构建的库,此变量还可声明二进制文件依赖哪些编译选项,从而保证编译成功。
建议使用此变量,而不要直接在 LOCAL_CPPFLAGS 定义中启用 -frtti 和 -fexceptions。
如使用 RTTI(运行时类型信息)
LOCAL_CPP_FEATURES := rtti
- 1
LOCAL_C_INCLUDES
头文件搜索路径,如
LOCAL_C_INCLUDES := sources/foo
- 1
注,在定义 LOCAL_CFLAGS, LOCAL_CPPFLAGS 前定义此变量。
LOCAL_CFLAGS
编译选项,如gcc -o main hello.c中的-o选项
可通过编写以下代码指定其他 include 路径,
LOCAL_CFLAGS += -I<path>
- 1
但使用 LOCAL_C_INCLUDES 更好,因为这样可通过ndk-gdb进行本地调试。
LOCAL_STATIC_LIBRARIES
存储当前模块依赖的静态库模块列表
LOCAL_WHOLE_STATIC_LIBRARIES
此变量是 LOCAL_STATIC_LIBRARIES的变体,表示链接器应将相关的库模块视为一个整体。如需了解有关整个存档的详细信息,请参阅 GNU 链接器关于 --whole-archive 标志的文档。
当多个静态库之间具有循环相依关系时,此变量很有用。 使用此变量构建共享库时,将会强制构建系统将所有对象文件从静态库添加到最终二进制文件。 但在生成可执行文件时不会发生这样的情况。
so依赖
推荐使用LOCAL_SHARED_LIBRARIES
LOCAL_SHARED_LIBRARIES
此模块在运行时依赖的共享库模块列表。 此信息在链接时需要,并且会在生成的文件中嵌入相应的信息。
LOCAL_PREBUILTS
引用另一个模块中的预建库
LOCAL_LDLIBS,LOCAL_LDFLAGS
相当于gcc中的-l选项。
在构建共享库或可执行文件时要使用的其他链接器标志列表。 使用 -l 前缀传递特定系统库的名称。
如指示链接器生成在加载时链接到 /system/lib/libz.so 的模块:
LOCAL_LDLIBS := -lz or LOCAL_LDFLAGS := -lz
- 1
LOCAL_EXPORT_CFLAGS
此变量用于记录C/C++编译器标志,将引用此模块的模块中LOCAL_CFLAGS添加指定的编译标志
如foo 和 bar,bra依赖于 foo:
include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_CFLAGS := -DFOO=1 include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_CFLAGS := -DBAR=2 LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在构建 bar.c 时会向编译器传递标志 -DFOO=1和-DBAR=2
此外,模块之间的关系也是可传递的:如果 zoo 依赖于 bar,后者又依赖于 foo,则 zoo 也会继承从 foo 导出的所有标志。
在本模块时不使用导出标志。 因此,在上面的示例中,构建 foo/foo.c 时不会将 -DFOO=1 传递到编译器。
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类似,用于指示构建系统将特定系统库名称传递到编译器。
需要在指定的每个库名称前面加上 -l。
注意,构建系统会将导入的链接器标志附加到本模块的 LOCAL_LDLIBS中,可以认为是功能更全的LOCAL_LDLIBS。
当模块 foo是静态库并且依赖于系统库的代码时,此变量通常很有用,可以使用LOCAL_EXPORT_LDLIBS导出相依关系。
include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_LDLIBS := -llog include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在此示例中,构建系统在构建 libbar.so 时,将在链接器命令的末尾放置 -llog。 这样会告知链接器,由于 libbar.so依赖于 foo,因此它也依赖于log
必须确保为目标ABI选择正确的预构建共享库版本。
例如,假设项目包含库 libfoo.so 两个版本:
armeabi/libfoo.so x86/libfoo.so
- 1
- 2
以下代码段显示如何使用 TARGET_ARCH_ABI,以便构建系统选择相应的库版本:
include $(CLEAR_VARS) LOCAL_MODULE := foo-prebuilt LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfoo.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
如果已指定 armeabi 作为 TARGET_ARCH_ABI 的值,则构建系统使用位于 armeabi 目录中的 libfoo.so 的版本。 如果已指定 x86 作为 TARGET_ARCH_ABI 的值,则构建系统使用 x86 目录中的版本。
不同 CPU 和架构的 ABI 设置。
CPU 和架构 |
设置 |
ARMv5TE |
armeabi |
ARMv7 |
armeabi-v7a |
ARMv8 AArch64 |
arm64-v8a |
i686 |
x86 |
x86-64 |
x86_64 |
mips32 (r1) |
mips |
mips64 (r6) |
mips64 |
参考网址
https://developer.android.com/ndk/guides/build.html?hl=zh-cn