Android技术内幕(系统卷)
上QQ阅读APP看书,第一时间看更新

1.2 获取和编译Android的源码

这一节我们将从零开始讲解如何取得并编译Android的源码及其常用工具。在这个过程中,你可以和本书一起准备好更加深入学习Android所必需的环境和工具,同时也能对Android源码的结构有一个整体的认识。

1.2.1 环境配置

由于我们需要对Android系统进行开发(移植),所以就必须下载Android的源代码。如果只是查看源代码,在线浏览即可,因为:第一,Android的源代码庞大,下载需要很长的时间;第二,官方所支持的Android源代码下载系统仅为Linux和Mac OS(当然,在Windows下也能下载,但是建议大家还是在Linux下下载)。这里我们将以Ubuntu 10.04为例,为大家详细介绍如何获取和编译Android的源代码。

我们从Android源码的官方网站(http://source.android.com)取得的关于配置需求的信息如下:

❑ 磁盘 需要6GB以上剩余空间

❑ Linux系统 Ubuntu 6.06以上版本

❑ Git工具 Git 1.5.4以上版本

❑ Java环境 JDK 5.0 update12以上版本

❑ Python Python 2.4以上版本

❑ 依赖的deb包 flex、bison、gperf、libsdl-dev、libesd0-dev、libwxgtk2.6-dev、build-essential、zip、curl

❑ 调试工具 valgrind

首先,准备好所需要的Linux操作系统以及磁盘空间。由于Ubuntu10.04系统自带的源里找不到JDK1.5的安装包,因此我们要做的第一件事是增加两个软件源(如果你能找到SDK1.5的安装包则不需要),如下:

deb http://tw.archive.ubuntu.com/ubuntu/  jaunty main restricted universe multiverse
deb-src http://tw.archive.ubuntu.com/ubuntu/ jaunty main restricted universe multiverse

然后,开始准备下载和编译Android源码的环境:

1)安装JDK1.5,在终端执行命令如下:

$ sudo apt-get install sun-java5-jdk

2)安装开发过程中需要的一些开发包,在终端执行以下命令:

$ sudo apt-get install git-core gnupg valgrind flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl libncurses5-dev zlib1g-dev

到这里,你已经准备好了获取Android源码所需要的环境,下面将开始获取Android源码。

1.2.2 获取Android源码

上一节已经准备好了 Android 源码开发所需的环境(请确保你所准备的环境无误),这一节将从Android源码服务器获取Android源码。由于Android的源码非常庞大,所以需要分为两部分来获取,分别是Android源码和Android内核(Android Linux Kernel)。

1.获取Android源码的步骤

如果要获取Android的完整源码,则需要按照如下步骤操作:

1)在用户目录下新建一个文件夹用来存放我们的Android目录,在终端执行如下命令:

mkdir Android
cd Android
mkdir bin
cd bin
curl http://android.git.kernel.org/repo > repo

稍等片刻即可完成,如图1-17所示。

图1-17 新建Android目录并初始化repo

2)建立存放Android源代码的目录source,并初始化版本,在终端执行如下命令:

sudo chmod a+x repo
cd
cd Android
mkdir source
cd source
../bin/repo    init -u
git://android.git.kernel.org/platform/manifest.git

这次需要等的时间可能会稍微长一点,如图1-18所示。

图1-18 建立source目录并初始化版本

3)之后要求输入用户名和邮箱地址,可随机输入。如果你需要向Google递交Bug,那么就输入你的 Google 账户,如图1-19所示。完成之后如果出现“repo initialized in /home/yarin/Android”,则表示初始化完毕,可以开始下载了。

图1-19 输入账户信息

注意 在显示初始化完成时,出现了一个路径,其中包含一个“yarin”字符,熟悉 Linux 的朋友可能已经明白为什么了,但这里还是说明一下。笔者在编写本书时,所使用的 Linux 系统的用户名为 yarin,而且所建立的用于存放 Android源码的目录也在用户目录中,以后还会多次出现,所以我们就不再详细解释了。

4)在终端执行如下命令,开始下载,如图1-20所示。

图1-20 开始下载Android源码

../bin/repo sync

注意 以上操作下载的是全部的源码,完全下载需要很长的时间。你也可以在../bin/repo init -u git://android.git.kernel.org/platform/manifest.git之后加入参数来指定下载某一部分源码。请参见在第二步之后出现的帮助信息(图1-18)。下载过程中可能会出现连接超时或者服务器繁忙的错误。

下载完成之后,你就得到了完整的Android源码。前面说过需要分为两部分来下载,并且这两部分是可同时下载,彼此不会受到任何影响。因此,为了节约时间,我们马上开始下载第二部分源码。

2.下载Android内核

在下载之前,我们先来看看Android内核属于哪一部分。首先,打开Android的开源工程官方网站(http://android.git.kernel.org/),Android内核主要包括了如图1-21所示的内容,这部分内容在我们使用“../bin/repo syn”下载的时候并不会被一起下载。如果你不需要对 Android内核进行修改和移植,可以不需要这部分内容,可以从Android模拟器中提取这部分内容的镜像。但是,本书需要关注这部分内容,并会对它进行详细分析,所以需要下载它。

图1-21 Android内核的部分源码

下载Android内核的具体步骤如下:

首先,进入上面我们创建的存放Android源码的目录,新建一个存放Android内核的目录“kernel”,并进入该目录,在终端执行如下命令,开始下载,如图1-22所示。

图1-22 下载Android内核

git clone git://android.git.kernel.org/kernel/common.git

下载Android内核时间会比下载Android源码的时间短很多。需要注意的是,Android内核的下载不能被中断,而Android源码支持断点下载。下一小节将为大家介绍编译的全过程。

下载完成之后在存放Android源码的目录中也许看不到任何内容,不必担心,那是因为下载的文件是隐藏的,你需要显示隐藏文件才能看见。下载完成之后,你可以在终端通过du -sk或du –sm命令检测文件夹的大小,以确保全部下载完成。如果你按本书的方式进行全部下载,那么总大小会接近4GB左右。另外,还需要编译和开发,所以请确保磁盘空间大于10GB。

1.2.3 编译Android的源码及其工具包

功夫不负有心人!终于下载完了,下面我们就开始编译。接下来的内容并不难,但是需要十分仔细(比如:不能多一个空格,也不能少某个步骤),否则编译过程中就可能有意想不到的“惊喜”降临到你身上。当然,你也不必紧张,只要按照下面的步骤一步一步操作,你自己编译的Android系统最后肯定能够运行成功。

1.编译Android源码

只要确保源码下载是完整的,编译过程中一般不会出错。笔者编译过很多次,都没有遇到意外情况,因为Android文件系统已经自带了经过优化的交叉编译工具,并且为所有的源码都提供了经过验证的makefile,所以系统的整体编译相对简单。首先需要从终端进入存放源码的目录,然后在终端输入如下命令,如图1-23所示。

图1-23 编译Android源码

make

在官方发布的版本中,基本功能都已经包含在makefile中。如果需要增加其他功能,可以到build/target/product/路径下修改相应的.mk文件。比如,如果要增加中文的输入法,可以按照下面的步骤进行:

cd /home/yarin/Android/source/build/target/product/
gedit generic.mk

打开这个文件后,在PRODUCT_PACKAGES中加入PinyinIME,然后保存并退出。这样,在整体编译的时候就会把中文输入法也编译进系统中。

编译的时间一般不会比下载的时间长,大约 2~3 小时就能完成。在默认情况下,编译完成之后,会在/home/yarin/Android/source 目录下生成一个 out 文件夹,生成的所有文件都放在该文件夹下,如果你想更改生成文件的目录,请参考./build/buildspec.mk.default 文件中的说明和选项(虽然很简单,但是笔者还是不建议大家去更改)。编译完成之后会在 out/target/product/generic/目录下生成一些.img镜像文件,比如system.img、userdata.img、ramdisk.img等,我们暂时不去测试生成的这些文件是否有效,因为稍后还需要编译 SDK 和其他工具,然后在模拟器上加载这些文件,这样才能测试其是否有效。

2.编译Android内核

我们在编译Android源码时并不会自动编译Android内核,因此需要手动来编译这部分内容,下面是编译内核的完整步骤。其实,操作与编译Linux内核一样,如果你非常熟悉,可以跳过这部分内容。

(1)确定内核版本

进入存放Android源码目录下的kernel目录,输入如下命令,查看kernel/common.git的默认主分支(本书所使用的源码显示为* android-2.6.35)。

git branch

然后,输入如下命令,显示所有head分支。

git branch -a

输入如下命令,选择当前最新的版本(本书选择了android-goldfish-2.6.35),其中goldfish是Android的模拟器模拟的CPU。

git checkout -b android-goldfish-2.6.35
origin/android-goldfish-2.6.35

此时,再次输入“git branch”命令,将显示我们所选择的最新分支,表示我们目前工作在这个被选择的分支上。

(2)设定环境变量

在编译 Android 内核时,需要使用交叉编译器。我们所下载的 Android 代码树中有一个prebuilt 文件夹,它包含了我们编译内核所需的交叉编译工具。因此,我们将其设置到环境变量中去,以方便使用。打开用户目录下的.bashrc文件,在后面添加如下代码:

export
PATH=$PATH:~/Android/source/prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin
export ARCH=arm

保存并退出,通过如下命令在用户目录下进行更新。因为有可能会因为添加之后没有及时更新而在编译时提示错误,如找不到交叉编译工具目录等(如果加入了环境变量仍在编译时提示找不到,可以尝试多更新几次)。

source ~/.bashrc

(3)设定交叉编译参数

在编译之前,我们还需要设定编译时所使用的交叉编译参数。具体步骤如下:

首先,打开kernel目录下的makefile文件,把CROSS_COMPILE指向Android源码提供的prebuilt中的arm-eabi编译器,如图1-24所示。

图1-24 设置CROSS_COMPILE

然后,找到如下代码行:

LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
$(call ld-option, -Wl$(comma)–build-id,))

将其删除(建议注释掉即可),并且添加一个空的LDFLAGS_BUILD_ID定义,如图1-25所示。

图1-25 设置LDFLAGS_BUILD_ID

(4)编译kernel镜像

一切设置完成之后,编译就很简单了,进入kernel目录,输入以下命令即可,如图1-26所示。

图1-26 编译kernel

$ cd ~/Android/source/kernel
$ make goldfish_defconfig
$ make

其中第2条命令是编译时默认的配置文件,如果不执行第二条命令,编译时可能会提示“找不到配置文件”。执行完第二条命令之后,会在kernel目录中多出一个.config文件。

编译完成之后,会在“kernel/arch/arm/boot/”目录下生成名为zImage的文件;稍后再运行系统时,就可以不使用Android SDK为我们提供的镜像文件,而加载该镜像以及上一节编译的系统镜像。既然要测试,就需要SDK,所以我们一下步将编译SDK。

3.编译SDK

要测试以上所编译的镜像和使用SDK进行应用开发,就需要编译SDK(当然,如果你不想编译,也可以直接到官方网站下载SDK,本书所使用的全部是通过源码编译出来的工具)。由于编译Android源码和内核时并不会自动编译SDK,因此下面我们就来手动编译SDK。

注意 如果需要,建议先编译ADT和单独模块,因为在编译ADT和单独模块时会清除编译SDK所生成的目录。

SDK的编译其实很简单,基本上不用配置,直接进入存放Android源码的目录,使用如下命令即可,如图1-27所示。

图1-27 编译SDK

$ make PRODUCT-sdk-sdk

注意 为了避免大家在搭建环境时编译的 SDK 无效,建议第一次不要直接使用make sdk命令,而使用如上所示命令。

编译时间稍长,编译后生成的 SDK 存放在 out/host/linux-x86/sdk/目录下。此目录下有android-sdk_eng.xxx_linux-x86.zip 和 android-sdk_eng.xxx_linux-x86 两个目录,android-sdk_eng.xxx_linux-x86就是我们需要使用的SDK。建议大家将SDK备份,因为我们马上会讲到的单独模块的编译可能会清除SDK目录。

4.编译单独模块

Android 允许我们对应用程序进行单独编译,但是编译后需要重新生成 system.img,下面就来学习如何单独编译应用程序。

首先,需要在源码存放目录中执行如下命令,注意:“.”后面有空格。

$ . build/envsetup.sh

完成之后就会多出以下命令,你可以加上-help查看其用法。

- croot:   Changes directory to the top of the tree.
- m:      Makes from the top of the tree.
- mm:     Builds all of the modules in the current directory.
- mmm:    Builds all of the modules in the supplied directories.
- cgrep:   Greps on all local C/C++ files.
- jgrep:   Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir:   Go to the directory containing a file.

然后,可以使用mmm来编译指定目录的模块。比如,我们要编译联系人应用,可以输入如下命令:

$ mmm packages/apps/Contacts/

编完之后生成两个文件:

out/target/product/generic/data/app/ContactsTests.apk
out/target/product/generic/system/app/Contacts.apk

最后,在运行模拟器之前,我们可以使用如下命令来重新生成system.img(即系统镜像)。

$ make snod

5.编译ADT

编译ADT的目的是使用Eclipse来开发应用程序,如果你不想编译,可以在Android官方网站下载,或者直接在线更新。这里,我们主要是告诉大家如何使用源码来编译ADT。

(1)配置环境

要编译ADT,首先应该确保系统中安装了Eclipse。如果没有安装,那么在编译ADT时会下载安装(但这需要取得root权限,否则无法安装,也就无法编译);如果需要自己手动下载并安装Eclipse,建议下载Eclipse3.4.0 RCP版本,因为自动下载安装时,下载的是Eclipse3.4.0 RCP;如果高于这个版本,编译过程中可能会出现找不到某些jar文件的情况,使得编译无法继续。编译ADT的过程很简单,但是经常会因为环境的准备不够充分而出错。所以,笔者建议大家下载官方的ADT或者让ADT编译过程自己下载合适的Eclipse版本,以减少错误的发生。

安装好 Eclipse 之后需要设置 ECLIPSE_HOME 的环境变量,否则编译程序会认为你没有装Eclipse,然后又帮你重新下载,我们不希望出现这样的情况。比如,笔者将Eclipsse安装在“~/Develop/eclipse-android_src/eclipse-RCP-3.4-linux-gtk”目录中,就在.bashrc文件中加入如下代码(如果不知道如何添加环境变量,请查看“Android kernel 编译”章节的环境配置,这里我们修改的当然是用户的环境变量,同样也可以修改整个系统的环境变量)。

export
ECLIPSE_HOME=$PATH:~/Develop/eclipse-android_src/eclipse-RCP-3.4-linux-gtk

(2)编译ADT

准备好环境之后,要编译ADT,进入Android源码存放目录,在终端输入如下命令即可,结果如图1-28所示。其中“/home/yarin/Android/source/adt”即我们存放编译的ADT的目录。

图1-28 编译ADT

sdk/eclipse/scripts/build_server.sh /home/yarin/Android/source/adt

编译完成之后,就会在我们指定的存放ADT的目录下找到生成的ADT,然后就可以使用该ADT搭建应用开发环境。

现在我们就已经编译了Android源码和所有需要使用的工具包,那么下面将测试我们的编译是否成功,即运行编译的Android系统。

1.2.4 运行Android系统

令人激动的时刻来临了,我们终于能看到自己所编译的Android系统了,下面就来一步一步对每个镜像进行测试。首先,需要成功地运行模拟器来加载镜像,模拟器在我们编译 SDK的时候已经生成了。

由于模拟器的运行需要创建AVD和sdcard卡(不是必需的),所以接下来首先进入SDK目录下的tools文件夹,然后在终端输入如下命令:

./mksdcard 512M sdcard.img
./android create avd --target 1 --name eyarin --sdcard sdcard.img
./android list avd

第一条命令用于创建一个512MB的sd卡。第二条命令用于创建一个AVD,并指定target为1,名字为eyarin,并且建在第一步创建的sd卡上。当提示是否需要自定义AVD时,我们这里选择“no”,即采用默认设置。第三条命令用于显示我们创建的AVD,用来检测是否创建成功,如图1-29所示。创建完成之后,会在当前用户目录中生成一个.android的目录,其中存放了我们所创建的AVD,你可以同时创建多个AVD。

图1-29 AVD操作

现在你已经可以使用如下命令运行一下模拟器看看效果了。

./emulator –avd eyarin

但是,这里运行的效果只是检测我们编译的 SDK 是否正确,并没有加载我们编译的系统镜像等。下面就分模块来进行测试,首先运行如下代码,加载我们编译的Android Kernel镜像,运行效果如图1-30所示。

图1-30 测试Android Kernel

./emulator -kernel
/home/yarin/Android/source/kernel/arch/arm/boot/zImage

该命令行参数-kernel指定了我们生成的Android Kernel镜像。确保无误之后,再来测试并加载系统镜像,在终端输入如下命令来加载我们编译的所有镜像,也包括上面测试的 Android Kernel,运行效果如图1-31所示。

./emulator -system /home/yarin/Android/source/out/target/product/generic/system. img
-data /home/yarin/Android/source/out/target/product/generic/userdata.img
-ramdisk  /home/yarin/Android/source/out/target/product/generic/ramdisk.img
-kernel /home/yarin/ Android/source/kernel/arch/arm/boot/zImage

心中颇有些喜悦,毕竟用上我们自己编译出来的Android操作系统了,继续学习,还会有更多令人愉快的事。你可以开发出优秀的应用程序,以及修改和移植该操作系统到其他的平台,但是在做这些事情需要首先完成开发环境的搭建,下一节就来学习如何搭建开发环境。