Android智能穿戴设备开发实战详解
上QQ阅读APP看书,第一时间看更新

3.4 编译源码

编译Android源码的方法非常简单,只需使用Android源码根目录下的Makefile,执行make命令即可轻松实现。当然在编译Android源码之前,首先要确定已经完成同步工作。进入Android源码目录使用make命令进行编译,使用此命令的格式如下所示。

编译Android源码可以得到“~/project/android/cupcake/out”目录,笔者的截图界面如图3-13所示。

图3-13 编译过程的界面截图

整个编译过程非常漫长,需要读者耐心等待。

3.4.1 搭建编译环境

在编译Android源码之前,需要先进行环境搭建工作。在接下来的内容中,以Ubuntu系统为例讲解搭建编译环境以及编译Android源码的方法。具体流程如下所示。

(1)安装JDK,编译Android 4.3的源码需要JDK1.6,下载jdk-6u23-linux-i586.bin后进行安装,对应命令如下:

(2)设置JDK环境变量,将如下环境变量添加到主文件夹目录下的.bashrc文件中,然后用source命令使其生效,加入的环境变量代码如下:

(3)安装需要的包,读者可以根据编译过程中的提示进行选择,可能需要的包的安装命令如下:

3.4.2 开始编译

当所依赖的包安装完成之后,即可开始编译Android源码,具体步骤如下所示。

(1)首先进行编译初始化工作,在终端中执行下面的命令:

或:

执行后将会输出:

(2)然后选择编译目标,命令是:

执行后会输出如下所示的提示信息:

(3)接下来开始编译代码,在终端中执行下面的命令:

其中“-j4”表示用四个线程进行编译。整个编译进度根据不同机器的配置而需要不同的时间。例如笔者电脑为intel i5-2300四核2.8,4G内存,经过近四小时才编译完成。当出现下面的信息时表示编译完成:

3.4.3 在模拟器中运行

以后在模拟器中运行的步骤就比较简单了,只需在终端中执行下面的命令即可:

运行成功后的效果如图3-14所示。

图3-14 在模拟器中的编译执行效果

3.4.4 常见的错误分析

虽然编译方法非常简单,但是作为初学者来说很容易出错,在下面列出了其中常见的编译错误类型。

(1)缺少必要的软件

进入Android目录下,使用make命令编译,可能会发现出现如下错误提示。

上述错误是因为缺少zlib1g-dev,需要使用apt-get命令从软件仓库中安装zlib1g-dev,具体命令如下所示。

同理需要安装下面的软件,否则也会出现上述类似的错误。

(2)没有安装Java环境JDK

当安装所有上述软件后,运行make命令再次编译Android源码。如果在之前忘记安装Java环境JDK,此时会出现很多Java文件无法编译的错误,如果打开Android的源码,可以看到在如下目录中下发现有很多Java源文件。

这充分说明在编译Android之前必须先安装Java环境JDK,安装流程如下所示。

● 从Oracle官方网站下载jdk-6u16-linux-i586.bin文件,然后安装。

在Ubuntu 8.04中,“/etc/profile”文件是全局的环境变量配置文件,它适用于所有的shell。在登录Linux系统时应该先启动“/etc/profile”文件,然后再启动用户目录下的“~/.bash_profile”、“~/.bash_login”或“~/.profile”文件中的其中一个,执行的顺序和上面的排序一样。如果“~/.bash_profile”文件存在话,则还会执行“~/.bashrc”文件。在此只需要把JDK的目录放到“/etc/profile”目录下即可。

● 重新启动机器,输入java-version命令,输出下面的信息则表示配置成功。

当成功编译Android源码后,在终端会输出如下提示。

3.4.5 实践演练——演示两种编译Android程序的方法

Android编译环境本身比较复杂,并且不像普通的编译环境那样只有顶层目录下才有Makefile文件,而其他的每个component都使用统一标准的Android.mk.Android.mk文件。不过这并不是我们熟悉的Makefile,而是经过Android自身编译系统的很多处理。所以说要真正弄清楚其中的联系还比较复杂,不过这种方式的好处在于,编写一个新的Android.mk给Android增加一个新的Component会变得比较简单。为了使读者更加深入地理解在Linux环境下编译Android程序的方法,在接下来的内容中将分别演示两种编译Android程序的方法。

1.编译Native C(本地C程序)的helloworld模块

编译Java程序可以直接采用Eclipse的集成环境来完成,实现方法非常简单,这里不再重复。接下来将主要针对C/C++进行说明,通过一个例子来讲解在Android中增加一个C程序的Hello World的方法。

(1)在“$(YOUR_ANDROID)/development”目录下创建一个名为“hello”的目录,并用“$(YOUR_ANDROID)”指向Android源代码所在的目录。

(2)在目录“$(YOUR_ANDROID)/development/hello/”下编写一个名为“hello.c”的C语言文件,文件hello.c的实现代码如下所示。

(3)在目录“$(YOUR_ANDROID)/development/hello/”下编写Android.mk文件。这是Android Makefile的标准命名,不能更改。文件Android.mk的格式和内容可以参考其他已有的Android.mk文件的写法,针对helloworld程序的Android.mk文件内容如下所示。

上述各个内容的具体说明如下所示。

● LOCAL_SRC_FILES:用来指定源文件用。

● LOCAL_MODULE:指定要编译的模块的名字,在下一步骤编译时将会用到。

● include $(BUILD_EXECUTABLE):表示要编译成一个可执行文件,如果想编译成动态库则可用BUILD_SHARED_LIBRARY,这些具体用法可以在“$(YOUR_ANDROID)/build/core/config.mk”查到。

(4)返回Android源代码顶层目录进行编译。

在此需要注意,make helloworld中的目标名helloworld就是上面Android.mk文件中由LOCAL_MODULE指定的模块名。最终的编译结果如下所示。

(5)如果和上述编译结果相同,则编译后的可执行文件存放在如下目录:

这样通过”adb push”可以将它传送到模拟器上,再通过”adb shell”登录到模拟器终端后即可执行。

2.手工编译C模块

在前面讲解了通过标准的Android.mk文件来编译C模块的具体流程,其实可以直接运用gcc命令行来编译C程序,这样可以更好地了解Android编译环境的细节。具体流程如下所示。

(1)在Android编译环境中,提供了“showcommands”选项来显示编译命令行,可以通过打开这个选项来查看一些编译时的细节。

(2)在具体操作之前需要使用如下命令把前面helloworld中的模块清除。

上面的“make clean-$(LOCAL_MODULE)”命令是Android编译环境提供的make clean的方式,上面代码中的参数“LOCAL_MODULE”值是helloworld。

(3)使用showcommands选项重新编译helloworld,具体命令如下所示。

从上述命令行可以看到,Android编译环境所用的交叉编译工具链如下所示。

prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc

其中参数“-I”和“-L”分别指定了所用的C库头文件和动态库文件路径分别是“bionic/libc/include”和“out/target/product/generic/obj/li”b,其他还包括很多编译选项以及-D所定义的预编译宏。

(4)此时可以利用上面的编译命令来手工编译helloworld程序,首先手工删除上次编译得到的helloworld程序。

然后再用gcc编译以生成目标文件。

如果此时与Android.mk编译参数进行比较,会发现上面主要减少了不必要的参数“-I”。

(5)接下来开始生成可执行文件。

在此需要特别注意的是参数“-Wl,-dynamic-linker,/system/bin/linker”,它指定了Android专用的动态链接器是“/system/bin/linker”,而不是平常使用的ld.so。

(6)最后可以使用命令file和readelf来查看生成的可执行程序。

这就是ARM格式的动态链接可执行文件,在运行时需要libc.so和libm.so。当提示“not stripped”时表示它还没被STRIP(剥离)。嵌入式系统中为节省空间通常将编译完成的可执行文件或动态库进行剥离,即去掉其中多余的符号表信息。在前面“make helloworld showcommands”命令的最后也可以看到, Android编译环境中使用了“out/host/linux-x86/bin/soslim”工具进行STRIP。