Android NDK 知识大全
引用
基本概念
- Android官方文档-概念,从这里可以发散出JNI、ABI等需要学习的概念,按文档学习即可
- Android Gradle插件节点定义
NDK安装配置
- Android官方指南
- Android Studio里NDK入门
- Android Studio下使用NDK,这里提供了一些gradle里直接设置toolchain,CFlags等的示例
编译选项
- Android NDK编译选项设置
- NDK中C++标准库、STL的配置;Include其他头文件
- Android Studio中对NDK进行设置
- Android官方指南之Androd.mk
- Android官方指南之Androd.mk
资料整理
无论是用Android Studio集成NDK,还是手动使用NDK命令行,有两个文件是必须的
- Android.mk: 在jni文件夹内必须创建这个文件,ndk-build命令也会首先查找此文件才能执行,文件里定义ndk编译的模块的名称、c/c++源代码,需要链接的库及构建选项(传递给编译工具gcc|clang等的参数)。
- Application.mk: 这个文件定义需要构建的模块和构建环境及工具(如果是集成在 android studio里,部分选项会被 gradle覆盖),包括以下:
- 需要编译的目标平台ABI
- 工具链接
- 需要包含的标准库(static and dynamic STLport or default system).
- 全局的编译选项
Android.mk
示例:
1 | LOCAL_PATH := $(call my-dir) # 必须要使用这个开头 |
变量和宏
命名规则:
- LOCAL_: NDK编译系统使用的,提供给用户设置值
- PRIVATE、NDK、APP:NDK编译系统内部使用,用户不要定义
- my-dir:一些小写名称,编译系统内部使用的
- MY_: 其它大写名称,是用户自定义(仅仅是推荐,用户可以使用其它前缀或不使用前缀)
NDK预定义变量
- CLEAR_VARS
include $(CLEAR_VARS)
- BUILD_SHARED_LIBRARY | BUILD_STATIC_LIBRARY
include $(BUILD_SHARED_LIBRARY)
- PREBUILT_SHARED_LIBRARY | PREBUILT_STATIC_LIBRARY
- TARGET_ARCH 目标cpu架构 取值arm、x86等
- TARGET_PLATFORM 目标android版本
TARGET_PLATFORM := android-22
- TARGET_ARCH_ABI 目标cpu及架构的全名 如:
TARGET_ARCH_ABI := arm64-v8a x86
,详表见官方文档中TARGET_ARCH_ABI一节。 - TARGET_ABI Android Api级别和ABI的组合
模块定义变量
这些变量是每个模块单独设置的,每个模块都需要做以下事情:
- 初使化模块变量或者用CLEAR_VARS清除
- 给一些变量赋值来描述模块的功能
- 使用NDK预定义变量中的BUILD_XXX来定义模块的格式(动态库、静态库等)
变量列表(粗体为必须):
- LOCAL_PATH 设置当前文件的路径,必须在Androd.mk的开头使用
LOCAL_PATH := $(call my-dir)
- LOCAL_MODULE 当前模块名称,不能用空格。除了
CLEAR_VARS
之外他要放在前面,不需要添加lib前缀和.so.a后缀。 - LOCAL_MODULE_FILENAME 模块文件名,可以指定一个新的模块文件名
- LOCAL_SRC_FILES 源文件文件列表
- LOCAL_CPP_EXTENSION 定义支持的c++源文件的后缀,一般来说可以用
LOCAL_CPP_EXTENSION := .cxx .cpp .cc
- LOCAL_CPP_FEATURES c++语言特性,如rtti、exceptions等
- LOCAL_C_INCLUDES 头文件默认是相对于NDK的根目录,如果相对于当前目录要用
LOCAL_C_INCLUDES := $(LOCAL_PATH)//foo
,需要设置在LOCAL_CFLAGS和LOCAL_CPPFLAGS 之前。ps:在windows上我想直接引用ndk目录,但使用了LOCAL_PATH后似乎就默认是当前目录了, 所以我最后使用了环境变量中的ndk目录。例:LOCAL_C_INCLUDES += $(ANDROID_NDK_HOME)\platforms\$(TARGET_PLATFORM)\arch-$(TARGET_ARCH)\usr\include
- LOCAL_CFLAGS 给c和c++的编译选项,从 android-ndk-1.5_r1开始,此选项仅适用于c语言
- LOCAL_CPPFLAGS 给c++的编译选项
- LOCAL_STATIC_LIBRARIES 当前模块依赖的静态库,如果当前模块也是静态库,这个表明引用当前模块的也自动依赖这些静态库。如果当前模块是可执行程序或动态库,则链接这些静态库。
- LOCAL_SHARED_LIBRARIES 运行时依赖的动态库,仅用在链接时
- LOCAL_LDLIBS 声明链接时引用的动态库,-l作为前缀,例
LOCAL_LDLIBS := -lz
声明需要链接/system/lib/libz.so
- LOCAL_LDFLAGS 链接参数,如果当前模块是是静态库,此参数无效
- LOCAL_ALLOW_UNDEFINED_SYMBOLS 当设置成true时,链接时不检查未定义的引用。如果当前模块是是静态库,此参数无效
- LOCAL_WHOLE_STATIC_LIBRARIES、LOCAL_ARM_MODE、LOCAL_ARM_NEON、LOCAL_DISABLE_NO_EXECUTE、LOCAL_DISABLE_RELRO、LOCAL_DISABLE_FORMAT_STRING_CHECKS、LOCAL_SHORT_COMMANDS、LOCAL_THIN_ARCHIVE、LOCAL_FILTER_ASM等 略,详见文档
- LOCAL_EXPORT_XXX系列指令
预定义函数
- my-dir 获取最后一个makefile的目录
- all-subdir-makefiles 返回所有子目录中的Android.mk
- this-makefile 当前makefile的路径
- parent-makefile 上级makefile的路径
- grand-parent-makefile 上上级makefile的路径
- import-module 使用方式
$(call import-module,<name>)
,在NDK_MODULE_PATH环境变量中查找指定名称的模块,然后inlucde它的Android.mk文件
Application.mk
示例:
1 | APP_ABI := x86 #只生成x86架构的CPU用的lib,要生成所有平台的可以改为all |
变量
- APP_PROJECT_PATH app的根目录,如果当前Application.mk放在
$PROJECT/jni/
下,则不需要设置此变量 - APP_MODULES 如果定义了此变量,则只编译指定的模块。模块名使用空格间隔
- APP_OPTIM release 或 debug ,默认值是release,如果在Android项目中定义了android:debuggable,,则默认是debug
- APP_CFLAGS 所有模块通用的c编译参数
- APP_CPPFLAGS 所有模块通用的c++编译参数
- APP_LDFLAGS 所有模块通用的链接参数
- APP_BUILD_SCRIPT ndk墨认在jin/下找文件Android.mk,可以使用这个变量指定编译脚本路径。
- APP_ABI 默认ndk生成的是armeabi,可以使用此变量来指定,例:
APP_ABI := armeabi armeabi-v7a x86 mips
- APP_PLATFORM 指定最小支持android版本,最好不要使用此选项,而是使用模块的build.gradle中的defaultConfig 或 productFlavors 中的minSdkVersion来指定。
- APP_STL 默认使用系统的std库,可以使用这个指定,可选的见官方文档
- NDK_TOOLCHAIN_VERSION 指定编译工具的版本,如果是
clang
就是引用Clang编译器,如果是4.9
这种,就是指定GCC的版本 - APP_SHORT_COMMANDS、APP_PIE、APP_THIN_ARCHIVE 略过,详见文档
gradle配置
如果不用android studio集成编译,而是使用ndk-build手动编译,则不需要配置gradle。
nkd的配置也分散在grale中android节点下面的各个节点中,主要在ndk,defaultConfig.externalNativeBuild.ndkBuild、externalNativeBuild.ndkBuild中,同时在sourceSets中对文件包含进行一些修改,flavor和build-types在不同的编译选项下打不同的包。
必须节点 android.externalNativeBuild
这里至少要包括cmake和ndkbuild结节之一,来表明ndk的编译系统。
然后子结点中,只能设置路径(path参数),只有当Android.mk或CMakeLists.txt不在默认目录上时使用此选项指定目录。
1 | android { |
android.defaultConfig.ndk
接口原型
1 | public interface CoreNdkOptions { |
注意这里也有cFlags和AbiFilters,这两项和下面的android.defaultConfig.externalNativeBuild重复,原因暂时我还不知道。
使用示例:
1 | ndk {//在android节点下面 |
android.defaultConfig.externalNativeBuild
构建参数,可以在每个Flavor里特殊定义。
见文档,接口原型为:
1 | public interface CoreExternalNativeNdkBuildOptions { |
在gradle里的使用方法见这里
buildTypes
1 | buildTypes { |