引用
基本概念
- 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
示例:
LOCAL_PATH := $(call my-dir) # 必须要使用这个开头
include $(CLEAR_VARS) # 必须,清除编译环境中所有的LOCAL变量,否则同时编译多个Module时不同的Android.mk里的LOCAL变量会相互影响
LOCAL_MODULE := mcpelauncher # 必须,定义模块名称
# 必须,源文件文件列表
LOCAL_SRC_FILES := nativepatch.c modscript.c modscript_nextgen.cpp modscript_ScriptLevelListener.cpp utf8proc_slim.c dobby.cpp
LOCAL_C_INCLUDES += E:\C_Shared\sdk\ndk-bundle\platforms\android-14\arch-arm\usr\include\
LOCAL_LDLIBS := -llog # 需要链接的NDK库
LOCAL_SHARED_LIBRARIES := tinysubstrate-bin
# 必须,包含定义好的一个文件,获取抽有LOCAL变量并决定构建信息
include $(BUILD_SHARED_LIBRARY)
$(call import-add-path, prebuilts) # 其它导入模块
$(call import-module, tinysubstrate-bin)
变量和宏
命名规则:
- 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
示例:
APP_ABI := x86 #只生成x86架构的CPU用的lib,要生成所有平台的可以改为all
#APP_STL := stlport_static
NDK_TOOLCHAIN_VERSION=4.7 #使用GCC4.7
APP_STL := gnustl_static #GNU STL
APP_CPPFLAGS := -fexceptions -frtti #允许异常功能,及运行时类型识别
APP_CPPFLAGS +=-std=c++11 #允许使用c++11的函数等功能
APP_CPPFLAGS +=-fpermissive #此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过编译;使用GNU STL时不用此项std::string 居然编译不通过!!
变量
- 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不在默认目录上时使用此选项指定目录。
android {
...
defaultConfig {...}
buildTypes {...}
// 这里至少要包括cmake和ndkbuild结节之一,来表明ndk的编译系统
externalNativeBuild {
// AndroidStudio2.2以上就添加了Cmake方式来编译NDK代码,但还是可以使用老方法
ndkBuild {
path "src/main/jni/Android.mk"
//path "prebuilts/tinysubstrate-bin/Android.mk"
}
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "CMakeLists.txt"
}
}
// If you want Gradle to package prebuilt native libraries
// with your APK, modify the default source set configuration
// to include the directory of your prebuilt .so files as follows.
sourceSets {
main {
jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
}
}
}
android.defaultConfig.ndk
接口原型
public interface CoreNdkOptions {
String getModuleName();
String getcFlags();
List<String> getLdLibs();
Set<String> getAbiFilters();
String getStl();
Integer getJobs();
}
注意这里也有cFlags和AbiFilters,这两项和下面的android.defaultConfig.externalNativeBuild重复,原因暂时我还不知道。
使用示例:
ndk {//在android节点下面
// 生成so的名字,是必须的
moduleName ="JNITest"
toolchain = 'clang'
CFlags.add('-std=c99')
// 添加依赖库
ldLibs.addAll(['android','OpenSLES', 'log'])
// 生成不同abi体系的so库
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64',
'mips', 'mips64'])
}
android.defaultConfig.externalNativeBuild
构建参数,可以在每个Flavor里特殊定义。
见文档,接口原型为:
public interface CoreExternalNativeNdkBuildOptions {
List<String> getArguments();
List<String> getcFlags();
List<String> getCppFlags();
Set<String> getAbiFilters();
Set<String> getTargets();
}
在gradle里的使用方法见这里
buildTypes
buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.pro'))
}
debug {
jniDebuggable true //有这个才会支持调试native 代码,这个放到release里一样能用
}
}