前言
怎么编写能在Android系统中运行的C/C++可执行程序,一般有一下两种方法:
- 通过谷歌提供的JNI机制
- 交叉编译(本文选用方式)
安卓一般搭载在ARM架构下,其文件格式与常规X86架构的电脑端有所不同,电脑端可运行的可执行文件无法在安卓端直接运行,这就需要进行交叉编译,将代码编译为ARM环境下能够运行的程序。
交叉编译有更大的自由度,可以使用NDK里提供的交叉编译工具,例如 android-ndk-r13b中交叉编译器 arm-linux-androideabi-gcc。
注:作者编程环境为 X86-64架构下的Ubuntu系统
C/C++链接库的处理---以mxml为例
额外的C/C++库也要经过交叉编译,X86 Linux环境下gcc编译的库在主程序交叉编译时很可能无法直接链接到主程序。
以我需要用到的xml文件解析库为例. 可选的有libxml2等,我这里选用 mxml 。 原因在于1.纯C语言开发,更小巧便捷 2. 其依赖的库更少,之后交叉编译更容易。具体过程如下:
-
从GitHub获取压缩包 mxml-3.2.tar.gz
-
下载后解压,运行 /.configure ,设定编译器选项 CC=arm-linux-androideabi-gcc 及 编译输出路径 —–prefix=XXX
-
运行命令 ./configure 得到Makefile。修改Makefile将 LIBS = -lpthread 改为 LIBS += -pthread (原因见下文常见问题)
-
运行 make。 运行后可能报错,依据报错对应再次修改Makefile(与testsml相关的部分不会影响最终生成的lib,可以删除或者手动建立空白文件)
-
在指定路径得到交叉编译的libmxml和mxml.h头文件
-
将头文件和链接库扔进交叉编译器的include路径即可.
交叉编译
我们用gcc编译程序时,可能会用到**“-I”(大写i),“-L”(大写l),“-l”(小写l)**等参数,下面做个记录:
举例:gcc -o example1 example1.c -I /usr/local/include/freetype2 -L/usr/local/lib -lfreetype -lm
上面这句话在编译example1.c 时,-I /usr/local/include/freetype2 将/usr/local/include/freetype2作为第一个寻找头文件的目录,参数-L 是**对应链接库地址。-**lfreetype ,l (小写的l)参数就是用来指定程序要链接的库,l参数紧接着就是库名。指定程序链接的库名是freetype.
本文的编译示例: arm-linux-androideabi-gcc test.c -I/home/exchange/android-ndk-r13b/my-toolchain/mxml/include -L/home/exchange/android-ndk-r13b/my-toolchain/mxml/lib -static -lmxml -o testand
常见问题
- 安卓环境下 lpthread 已经集成到核心库,libc,bionic提供了对pthreads的内置支持。所以对交叉编译找不到 lpthread 库的情况,将Makefile对应位置链接库对应位置改为 += -pthread
- 交叉编译链接阶段经常找不到特定的头文件或者链接库,通过以下命令可以查看交叉编译器默认的头文件地址和链接库地址
echo 'main(){}' | /home/exchange/android-ndk-r13b/my-toolchain/bin/arm-linux-androideabi-gcc -E -v -