目录
Android NDK基础和NDK独立编译SO环境搭建
1. Android NDK基础
1.1 简介
1.2 结构
1.3 目录详解
1.4 工具详解
1.4.1 ndk-build.cmd
1.4.2 Android.mk构建规则脚本
1.4.3 Application.mk构建配置脚本
1.5 Android NDK其他
1.5.1 Android版本号
1.5.2 精简内容
1.5.3 免责声明
1.5.4 文档
1.5.5 发现GUG
2. Android NDK安装调试
2.1 NDK官网下载
2.2 NDK安装配置
2.2.1 解压压缩包
2.2.2 配置环境变量
2.3 编译简单样例测试
2.3.1 新建jni文件夹
2.3.2 新建编译文件
2.3.3 编译调试
2.3.4 编译生成的文件
2.4 XXX模组编译so
2.4.1 搭建XXX模组编译so的NDK环境
2.4.2 调试过程中添加打印信息的方法
2.5 so文件烧录调试
本目录下的是安卓ndk的一个副本,主要用具构建安卓native构件.
文件 |
作用 |
build |
构建脚本的储存目录 |
meta |
配置文件 |
platforms |
不同Android版本下的链接库 |
prebuilt |
预构建文件,主要用于远程调试 |
python-packages |
python文件夹,python构建的工具集 |
shader-tools |
影子工具(没弄懂) |
simpleperf |
cpu性能分析工具 |
source |
NDK自带的一些源代码库 |
sysroot |
所有头文件的位置 |
toolchains |
针对不同的平台的不同的构建工具链 |
wrap.sh |
打包的脚本 |
ndk-build.cmd |
构建工具 |
ndk-gdb.cmd |
调试工具 |
ndk-stack.cmd |
堆栈追踪工具 |
ndk-which.cmd |
在调试时选择ABI和调试工具(弃用) |
source.properties |
ndk版本信息 |
简介:构建ndk工程的工具,是对一系列make脚本的包装
内部原理:
$GNUMAKE-f\
make构建工具-f\
命令行调用:在构建目录下运行ndk-build脚本
ndk-build可选参数参考网页:
选项 |
含义 |
clean |
清除之前生成的所有二进制文件 |
V=1 |
显示构建命令 |
-B |
强制执行完整构建 |
NDK_DEBUG=1 |
强制执行可调试构建 |
NDK_DEBUG=0 |
强制发布build |
NDK_HOST_32BIT=1 |
始终使用32位构建工具 |
NDK_APPLICATION_MK= |
使用指定的Application.mk文件 |
-C |
构建指定project目录下的项目 |
ndk-build构建工具需要使用:Android.mk和Application.mk
参考网页
该脚本定义构建规则和构建目标,是构建静态库,动态库,还是构建可执行文件.
同时定义依赖项和导出项.
最基本构建模块:
LOCAL_PATH:=$(callmy-dir)#设定项目目录
include$(CLEAR_VARS)#包含清除变量的系统脚本
LOCAL_MODULE:=hello-jni#声明构建的模块名称
LOCAL_SRC_FILES:=hello-jni.c#声明要构建的文件
include$(BUILD_SHARED_LIBRARY)#声明构建共享库文件
变量与宏:
include变量(包含指定的构建模式):
#使用方式:
include$(VARS)
目标信息变量:
构建系统会根据ABI的不同重新解析Android.mk,进行重新构建.
ABI架构对应表
ABI(CPU和架构) |
设置名 |
ARMv7 |
armeabi-v7a |
ARMv8AArch64 |
arm64-v8a |
i686 |
x86 |
x86_64 |
x86_64 |
实例:
#cpu系列
ifeq($(TARGET_ARCH),arm)
#dosomething
endif
#版本信息
ifeq($(TARGET_PLATFORM),android-22)
#dosomething
endif
#关于ABI
ifeq($(TARGET_ARCH_ABI),arm64-v8a)
#dosomething
endif
#使用目标构建
ifeq($(TARGET_ABI),android-23-arm64-v8a)
#dosomething
endif
模块描述变量:
LOCAL_CPP_FEATURES:=exceptionsrtti
LOCAL_LDLIBS:=-lz
说白了,就是链接指定的Android库.AndroidNDK原生API库
注意:该构建变量只对可执行文件和共享库有用,对于静态库无用.
LOCAL_LDFLAGS+=-fuse-ld=bfd
静态库不适用此变量.
LOCAL_ARM_MODE:=arm
LOCAL_SRC_FILES=foo.c.neonbar.czoo.c.arm.neon
include$(BUILD_SHARED_LIBRARY)
在这里,构建系统在构建bar.c时会向编译器传递-DFOO=1和-DBAR=2标记。它还会在模块的LOCAL_CFLAGS前面加上导出的标记,以便您轻松进行替换。
此外,模块之间的关系也具有传递性:如果zoo依赖于bar,而后者依赖于foo,那么zoo也会继承从foo导出的所有标记。
最后,构建系统在执行局部构建时(即,构建要导出标记的模块时),不使用导出的标记。因此,在以上示例中,构建系统在构建foo/foo.c时不会将-DFOO=1传递到编译器。如需执行局部构建,请改用LOCAL_CFLAGS。
include$(BUILD_SHARED_LIBRARY)
在此示例中,构建系统在构建libbar.so时,将在链接器命令的末尾指定-llog。这样就会告知链接器,由于libbar.so依赖于foo,因此它也依赖于系统日志记录库。
bar.S--2-->$OBJS_DIR/bar.S--3-->$OBJS_DIR/bar.o
“1”对应于编译器,“2”对应于过滤器,“3”对应于汇编程序。过滤器必须是一个独立的shell命令,它接受输入文件名作为第一个参数,接受输出文件名作为第二个参数。
例如:
myasmfilter$OBJS_DIR/foo.S.original$OBJS_DIR/foo.S
myasmfilterbar.S$OBJS_DIR/bar.S
NDK提供的宏函数:
LOCAL_PATH:=$(callmy-dir)
由于GNUMake的工作方式,这个宏实际返回的是构建系统解析构建脚本时包含的最后一个makefile的路径。因此,包括其他文件后就不应调用my-dir。例如:
LOCAL_PATH:=$(callmy-dir)
#...declareonemodule
include$(LOCAL_PATH)/foo/`Android.mk`
LOCAL_PATH:=$(callmy-dir)
#...declareanothermodule
这里的问题在于,对my-dir的第二次调用将LOCAL_PATH定义为PATH/foo,而不是PATH/foo,而不是PATH,因为这是其最近的include所指向的位置。
在Android.mk文件中的任何其他内容后指定额外的include可避免此问题。
例如:
LOCAL_PATH:=$(callmy-dir)
#...declareonemodule
LOCAL_PATH:=$(callmy-dir)
#...declareanothermodule
#extraincludesattheendoftheAndroid.mkfile
include$(LOCAL_PATH)/foo/Android.mk
如果以这种方式构造文件不可行,请将第一个my-dir调用的值保存到另一个变量中。
例如:
MY_LOCAL_PATH:=$(callmy-dir)
LOCAL_PATH:=$(MY_LOCAL_PATH)
#...declareonemodule
include$(LOCAL_PATH)/foo/`Android.mk`
LOCAL_PATH:=$(MY_LOCAL_PATH)
#...declareanothermodule
$(callimport-module,
在此示例中,构建系统在NDK_MODULE_PATH环境变量所引用的目录列表中查找具有标记的模块,并且自动包括其Android.mk文件。
概览:
Application.mk指定ndk-build的项目级设置。默认情况下,它位于应用项目目录中的jni/Application.mk下。
注意:其中许多参数也具有模块等效项。例如,APP_CFLAGS对应于LOCAL_CFLAGS。无论何种情况下,特定于模块的选项都将优先于应用级选项。对于标记,两者都使用,但特定于模块的标记将后出现在命令行中,因此它们可能会替换项目级设置。
指令集 |
值 |
32位ARMv7 |
APP_ABI:=armeabi-v7a |
64位ARMv8(AArch64) |
APP_ABI:=arm64-v8a |
x86 |
APP_ABI:=x86 |
x86_64 |
APP_ABI:=x86_64 |
所有支持的ABI |
APP_ABI:=all |
详细ABI参见这里
您也可以指定多个值,方法是将它们放在同一行上,中间用空格分隔。例如:
APP_ABI:=armeabi-v7aarm64-v8ax86
APP_CPPFLAGS:=-DFOO
APP_CXXFLAGS:=-DBAR
以上配置将导致编译命令类似于clang++-DFOO-DBAR,而不是clang++-DBAR-DFOO。
注意:APP_WRAP_SH_
版本号 |
API版本号 |
2.3.3-2.3.7 |
10 |
4.0.3-4.0.4 |
15 |
4.1.x |
16 |
4.2.x |
17 |
4.3 |
18 |
4.4 |
19 |
5.0 |
21 |
5.1 |
22 |
6.0 |
23 |
7.0 |
24 |
7.1 |
25 |
8.0 |
26 |
8.1 |
27 |
9 |
28 |
10 |
29 |
11 |
30 |
注意
ndk-witch.cmd是适用于GCC系列的调试选择工具,对于LLVM不适用,而现在ndk又使用LLVM做编译器,弃用了GCC,那么这个调试工具之应用于GCC所构建的项目之中,因此,其也要被废弃。
本副本只是一个个人研究的版本,如若用于商业用途,请使用Android官方发布的正式版本。请勿将本版本ndk用于商业用途。
NDK文档,教程,还有API的信息可以访问我们的网站.
NDK代码示例可以访问GitHub.
关于AndroidStudio的信息可以访问AndroidStudio网页.
只要能运行,谁在乎???
NDKbugsshouldbefiledonGitHub.
AndroidStudioandGradlebugsshouldbefiledintheAndroidStudioBugTracker.Forthefastestresponse,makesureyoufollowtheirguideonFilingBugs.
首先去官网下载需要安装的Android NDK版本:
1.官网NDK 下载地址
https://developer.android.google.cn/ndk/downloads/
2.各NDK版本直接下载地址
最新稳定版本r22
https://dl.google.com/android/repository/android-ndk-r22-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r22-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r22-linux-x86_64.zip
最新LTS版本r21e
https://dl.google.com/android/repository/android-ndk-r21e-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21e-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip
其他历史r21版本
https://dl.google.com/android/repository/android-ndk-r21d-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21d-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21d-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21c-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21c-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21c-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r21-linux-x86_64.zip
历史版本:
https://dl.google.com/android/repository/android-ndk-r20b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r20b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r20b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r19c-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r19c-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r19c-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r19c-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r18b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r18b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r18b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r18b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r17c-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r17c-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r17c-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r16b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r16b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r16b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r15c-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r15c-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r15c-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r14b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r14b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r14b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r13b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r13b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r13b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r12b-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r12b-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r12b-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r12b-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r11c-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r11c-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r11c-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r11c-linux-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r10e-windows-x86.zip
https://dl.google.com/android/repository/android-ndk-r10e-windows-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r10e-darwin-x86_64.zip
https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
下面以64位Windows版的android-ndk-r20b为例对NDK的安装配置进行介绍。
先将下载的android-ndk-r20b-windows-x86_64.zip压缩包解压。
解压后android-ndk-r20b目录结构如下图所示:
将NDK路径添加到环境变量中并点击确定(修改完成后重启电脑或者进入DOS命令提示符,输入:set PATH=C: 即可生效)。
在NDK根目录下新建个jni命名的文件夹
在jni命名的文件夹中新建三个文件:hello-jni.cpp, Android.mk, Application.mk
hello-jni.cpp内容:
#include
int main()
{
printf("Hello Android!\n");
return 0;
}
Android.mk内容:
# 一个Android.mk file首先必须定义好LOCAL_PATH变量。
# 它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’,
# 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
LOCAL_PATH := $(call my-dir)
# CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量
#(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。
#这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
include $(CLEAR_VARS)
# LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。
# 注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
LOCAL_MODULE := hello-jni
# LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,
# 因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。
LOCAL_SRC_FILES := hello-jni.cpp
# BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译
# BUILD_SHARED_LIBRARY 表示动态链接库的方式进行编译
include $(BUILD_EXECUTABLE)
Application.mk内容:
#### 1.Android API 级别版本号
APP_PLATFORM := android-29
#### 2.CPU 与指令集的每种组合专属的应用二进制接口 (ABI):
#### armeabi-v7a arm64-v8a x86 x86_64 all
APP_ABI := all
#### 3.C++标准库 libc++ c++_shared c++_static system none等
APP_STL := c++_shared
#### 4.编译版本release 或 debug,默认为debug
APP_OPTIM := release
1、进入NDK环境的build目录。
2、在build目录路径栏输入cmd后按Enter键,弹出build目录路径的DOS界面。
3、在DOS界面命令行输入ndk-build,按Enter键NDK即可运行编译hell-jni.cpp文件。如下图即为正常编译,否则报错则需分析定位解决后再运行编译。
NDK运行编译完成后会在根目录下生成libs文件夹,并在里面生成相应APP_ABI的编译文件夹如下图所示:
在相应APP_ABI的编译文件夹下面有编译生成的二进制文件,比如本例x86_64文件夹中如下图所示:
如“2.3 编译简单样例测试所”所述流程,同样可搭建XXX模组编译so环境。搭建XXX模组的jni文件夹如下图所示:(略)
其他依赖的文件都统一放在jni目录下的include目录中,根据待编译源码分析添加。
目前在搭建的NDK环境中编译出的XXX模组标定数据so文件如下图所示:(略)
如果编译so调试过程中遇到比较复杂的问题,需要添加打印信息分析定位。在 Android.mk里打印信息有三种方法:
a. $(info string) 打印正常输入信息
b. $(warning string) 打印警告信息
c. $(error string) 打印错误信息
以上三种打印方式:
a. 各个打印方式的string里都可以带上变量;
b. 如果遇到error打印,将会停止编译进程。
举例说明各个打印函数的方法,以下以packages/apps/Music/Android.mk为例,内容如下:
LOCAL_PATH:= $(call my-dir)
$(warning "Warning Just for Test LOCAL_PATH is $(LOCAL_PATH)")
$(info "Info Just for Test LOCAL_PATH is $(LOCAL_PATH)")
include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) src/com/android/music/IMediaPlaybackService.aidl
LOCAL_PACKAGE_NAME := Music
LOCAL_SDK_VERSION := current
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
首先得准备好Qualcomm/MTK手机进行刷机和解锁,详见:(略)
手机解锁脚本:(略)
然后将编译好的so文件烧录到手机中,进行拍照并抓取log。
烧录so文件命令:
adb boot
adb remount
adb push src/xxx.so vendor/lib64
烧录so脚本:(略)
导出照片命令:
adb boot
adb remount
adb pull sdcare/DCIM/Camera dst/
导出照片脚本:(略)
抓取log命令:
adb boot
adb remount
adb pull data/debuglogger/mobilelog/ dst/
抓取log脚本:(略)
其他问题说明:
q1:为避免混乱将依赖的头文件统一放在jni目录下新建的include目录中,编译找不到头文件。 s1:Android.mk中添加LOCAL_C_INCLUDES += $(LOCAL_PATH)/include/)
q2:xxx.h依赖的yyy.h头文件找不到?s1:将xxx.h中依赖头文件yyy.h的路径修改为NDK环境下的路径。
q3:undefined reference to ‘xxx’问题 s3:在根目录Android.mk中添加LOCAL_ALLOW_UNDEFINED_SYMBOLS := true可以规避(实际上这样解决有问题,曾遇到根因是子目录Android.mk脚本中LOCAL_SRC_FILES += $($(wildcard $(LOCAL_PATH)/*.cpp):$(LOCAL_PATH)/%=%)未能实现,手动实现LOCAL_SRC_FILES 添加最终解决问题)
q4:将NDK编译好的xxx模组的so烧录手机后相应的app挂掉?s4:APP_ABI和APP_PLATFORM等系统架构变量设置要跟手机版本信息对齐匹配,将xxx模组直接相关的所有so和NDK编译生成的动态库共so全部烧录到手机vendor/lib64中,相应的app功能正常了 (首先得保证源码逻辑正确,否则先得解决源码逻辑问题否则也会导致相应的app挂掉)
参考
NDK官方指南:
https://developer.android.google.cn/ndk/guides
Gitee NDK介绍:
https://gitee.com/sipu/Android_NDK?skip_mobile=true#%E7%B2%BE%E7%AE%80%E5%86%85%E5%AE%B9