**注意:**做为
Android
开发人员,心里多少有对源码的向往,研究源码能够说是开发人员的荣耀,彰显了对系统的认知度,本文适用于Ubuntu 16.04
上编译Android 6.0.*
及以上版本,其余状况请酌情处理,自行参考官网,源码针对系统8.0分析。python
@(源码系列)linux
##摘要 android源码编译的四个流程:android
下文也将按照该流程讲述.git
##1.源码下载 因为墙的缘由,不少人采用国内的镜像源进行下载。不过笔者经过设置hosts能够在官方地址上下载源码,配置项以下:shell
110.4.24.178 www.googlesource.com 110.4.24.178 android.googlesource.com 110.4.24.178 dl-ssl.google.com 110.4.24.178 gerrit.googlesource.com 110.4.24.178 www.google.com 110.4.24.178 google.com 110.4.24.178 google.com.hk 110.4.24.178 www.google.com.hk 110.4.24.178 chromium.googlesource.com
地址查询来源,参考google代理hosts查询,按期更新,用起来很是方便。 在ubuntu下,下载代码仍是比较简单的,步骤分为4步:ubuntu
1.下载repo toolapi
mkdir ~/bin PATH=~/bin:$PATH curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo chmod a+x ~/bin/repo
2.初始化repo下载代码,须要注册谷歌帐号,与本身git配置相对应,注意姓名的排序,lastName在前。bash
mkdir WORKING_DIRECTORY cd WORKING_DIRECTORY git config --global user.name "Your Name" git config --global user.email "you@example.com"
3.repo初始化,选择须要下载的分支markdown
repo init -u https://android.googlesource.com/platform/manifest # 若是不填写分支默认下载master或经过-b切换至对应的分支 repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r1
4.同步android源码网络
repo sync
**提示:**这里,来简单的介绍下repo工具,咱们知道AOSP项目由不一样的子项目组成,为了方便进行管理,Google采用Git对AOSP项目进行多仓库管理。AOSP项目当前全部的分支列表参看分支列表。
之后若是须要同步最新的远程代码到本地,也只须要执行该命令便可。在同步过程当中,若是由于网络缘由中断,使用该命令继续同步便可。不出意外,5个小时即可以将所有源码同步到本地。
**提示:**必定要肯定代码彻底同步了,否则在下面编译过程出现的错误会让你痛不欲生,不肯定的童鞋能够多用repo sync同步几回。
因为网络缘由,在使用repo sync
同步代码的过程当中会屡次出错,总不能时时刻刻刻盯着,能不能在同步失败的状况下,自动重试呢?固然能够,咱们能够写一个简单的shell脚本:
#!/bin/bash #FileName source_asyn.sh PATH=~/bin:$PATH # 注意修改为你要编译的版本,固然也能够不填 repo init -u https://android.googlesource.com/platform/manifest repo sync while [$? = 1]: do echo "=========download failed,again============" sleep 5 repo sync done
将该文件保存在源码目录下,也就是咱们的source目录,而后执行该脚本便可,如今能够安心的等待源码下载完成了。
注意:因为
.repo
目录是隐藏目录,所以在下载完成以前你是不看到啥东西的。
##2.源码编译 ###2.1编译环境 源码下载完成后,就能够构建编译环境了。在开始以前,咱们先来看看一些编译要求:
硬件
磁盘空间越多越好,至少在100GB以上
软件
|Android版本| 编译要求的Ubuntu最低版本| 编译要求的JDK版本| |--| |Android 6.0至AOSP master| Ubuntu 14.04|OpenJDK 8| |Android 5.x至AOSP 6.0| Ubuntu 12.04|OpenJDK 7| |Android 2.3.x至Android 5.0| Ubuntu 12.04|Oracle JDK 6| |Android 1.5至Android 2.2.x| Ubuntu 10.04|Oracle JDK 5|
更具体的能够参看:Google源码编译要求
我如今在Ubuntu 16.04下编译AOSP主线代码,所以须要安装OpenJDK 8,执行命令以下:
sudo apt-get install openjdk-8-jdk
其它需求
sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g++-multilib sudo apt-get install -y git flex bison gperf build-essential libncurses5-dev:i386 sudo apt-get install tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386 sudo apt-get install dpkg-dev libsdl1.2-dev libesd0-dev sudo apt-get install git-core gnupg flex bison gperf build-essential sudo apt-get install zip curl zlib1g-dev gcc-multilib g++-multilib sudo apt-get install libc6-dev-i386 sudo apt-get install lib32ncurses5-dev x11proto-core-dev libx11-dev sudo apt-get install libgl1-mesa-dev libxml2-utils xsltproc unzip m4 sudo apt-get install lib32z-dev ccache
###2.2初始化编译 首先初始化编译环境,确保上述过程完整完成,接下来咱们须要初始化编译环境,命令以下:
source build/envsetup.sh
不难发现该命令只是引入了其余执行脚本,至于这些脚本作什么,目前不在本文中细说.。该命令执行成功后,咱们会获得了一些有用的命令,好比最下面要用到的lunch命令。
###2.3编译源码 初始化编译环境以后,就进入源码编译阶段。这个阶段又包括两个阶段:选择编译目标和执行编译。 ####2.3.1选择编译目标 经过lunch指令设置编译目标,所谓的编译目标就是生成的镜像要运行在什么样的设备上。这里咱们设置的编译目标是aosp_arm64-eng,所以执行指令:
lunch aosp_arm64-eng
编译目标格式说明 编译目标的格式:BUILD-BUILDTYPE,好比上面的
aosp_arm-eng
的BUILD是aosp_arm
,BUILDTYPE是eng
。
什么是BUILD
BUILD指的是特定功能的组合的特定名称,即表示编译出的镜像能够运行在什么环境。其中aosp(Android Open Source Project)表明Android开源项目;arm表示系统是运行在arm架构的处理器上;arm64则是指64位arm架构;处理器x86则表示x86架构的处理器;此外,还有一些单词表明了特定的Nexus设备,下面是经常使用的设备代码和编译目标,更多参考官方文档。
|受型号| 设备代码| 编译目标| |:--| |Nexus 6P| angler| aosp_angler-userdebug| |Nexus 5X| bullhead| aosp_bullhead-userdebug| |Nexus 6| shamu| aosp_shamu-userdebug| |Nexus 5| hammerhead| aosp_hammerhead-userdebug|
提示:若是你没有Nexus设备,那么一般选择arm或者x86便可
什么是BUILDTYPE
BUILD TYPE则指的是编译类型,一般有三种:
了解编译目标的组成以后,咱们就能够根据本身目前的状况选择了。那不知道编译目标怎么办?咱们只须要执行不带参数的lunch
指令,稍后控制台会列出全部的编译目标,以下:
接着咱们只须要输入相应的数字便可。 来举个例子:你没有Nexus设备,只想编译完后运行看看,那么就能够选择aosp_arm-eng
。(我在ubuntu 16.04(64位)中编译完成后启动虚拟机时,卡在黑屏,尝试编译aosp_arm64-eng
解决。所以这里我使用了aosp_arm64-eng
)
####2.3.2开始编译 经过make指令进行代码编译,该指令经过-j
参数来设置参与编译的线程数量,以提升编译速度。好比这里咱们设置8个线程同时编译:
make -j8
须要注意的是,参与编译的线程并非越多越好,一般是根据你机器cup的核心来肯定:core*2
,即当前cpu的核心的2倍。好比我如今的笔记本是双核四线程的,所以根据公式,最快速的编译能够make -j8
。
经过cat /proc/cpuinfo查看相关cpu信息
若是一切顺利的化,在几个小时以后,即可以编译完成。看到
### make completed successfully (04:16:38(hh:mm:ss)) ###
表示你编译成功了。
###2.4运行模拟器 在编译完成以后,就能够经过如下命令运行Android虚拟机了,命令以下:
source build/envsetup.sh lunch(选择刚才你设置的目标版本,好比这里了我选择的是2) emulator
若是你是在编译完后马上运行虚拟机,因为咱们以前已经执行过source及lunch命令了,所以如今你只须要执行命令就能够运行虚拟机:
emulator
不出意外,在等待一会以后,你会看到运行界面:
**补充:**既然谈到了模拟器运行,这里咱们顺便介绍模拟器运行所须要四个文件:
Linux Kernel
、system.img
、userdate.img
、ramdisk.img
若是你在使用 lunch命令时选择的是aosp_arm-eng,那么在执行不带参数的emualtor命令时,Linux Kernel默认使用的是/source/prebuilds/qemu-kernel/arm/kernel-qemu
目录下的kernel-qemu文件;而android镜像文件则是默认使用source/out/target/product/generic
目录下的system.img
,userdata.img
和ramdisk.img
,也就是咱们刚刚编译出来的镜像文件。
上面我在使用lunch命令时选择的是aosp_arm64-eng,所以linux默认使用的/source/prebuilds/qemu-kernel/arm64/kernel-qemu
下的kernel-qemu,而其余文件则是使用的source/out/target/product/generic64
目录下的system.img
, userdata.img
和ramdisk.img
。
固然emulator指令容许你经过参数制定使用不一样的文件,具体用法能够经过emulator --help
查看。
###2.5模块编译 除了经过make命令编译能够整个android源码外,Google也为咱们提供了相应的命令来支持单独模块的编译。编译环境初始化(即执行source build/envsetup.sh)以后,咱们能够获得一些有用的指令,除了上边用到的lunch,还有如下:
- 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指令就是用来编译指定目录。一般来讲,每一个目录只包含一个模块。好比这里咱们要编译Launcher2模块,执行指令:
mmm packages/apps/Launcher2/
稍等一会以后,若是提示:
### make completed success fully ###
即表示编译完成,此时在out/target/product/gereric/system/app
就能够看到编译的Launcher2.apk文件了。
从新打包系统镜像
编译好指定模块后,若是咱们想要将该模块对应的apk集成到系统镜像中,须要借助make snod
指令从新打包系统镜像,这样咱们新生成的system.img
中就包含了刚才编译的Launcher2模块了,重启模拟器以后生效。
单独安装模块
咱们在不断的修改某些模块,总不能每次编译完成后都要从新打包system.img,而后重启手机吧?有没有什么简单的方法呢? 在编译完后,借助adb install命令直接将生成的apk文件安装到设备上便可,相比使用make snod,会节省不少事件。
补充:咱们来介绍
out/target/product/generic/system
目录下的内容: 系统自带apk文件:out/target/product/generic/system/apk
目录下; 系统可执行文件:out/target/product/generic/system/bin
目录下; 动态连接库:out/target/product/generic/system/lib
目录下; 硬件抽象层文件:out/targer/product/generic/system/lib/hw
目录下。
###2.6错误合集 1. 提示:error processing archive /var/cache/apt/archives/lib32z1-dev_
在安装依赖的时候有一个依赖不能顺利安装:
sudo apt-get install lib32z-dev ccache
这里笔者也是安装你们推荐的依赖安装,若是出现任何不能安装的依赖都会去解决一下,解决办法为:
sudo dpkg -i --force-overwrite <filename> # 以下例 sudo dpkg -i --force-overwrite /var/cache/apt/archives/lib32z1-dev_1%3a1.2.8.dfsg-2ubtuntu4_amd64.deb # 而后执行 sudo apt-get -f install
问题顺利解决。
##3.源码调试 咱们已经描述了Android源码在Ubuntu 16.0.4上下载和编译的过程,目前咱们已经顺利的编译出android的成果文件:
下载源码初衷咱们是为了调试代码,查看其中的运行机制和关键过程,理解其精髓。好比咱们对PMS过程不是很了解,如何打个断点查看一下,亦或是根据本身思想改进一下,都是很是值得尝试的。下面咱们来讲说如何调试源码,一样这里的工做平台仍是ubuntu 16.04和Android Studio。
**参考:**源码中developent/tools/idegen/README文档
###3.1调试准备 在源码中,存在idegen模块,该模块专门用来为idea工具生成系统源码的project。在开始调试以前,须要确保当前已经编译过Android源码了,若是没有,跳转至第二章编译源码,时间大概4-5个小时。准备过程分为以下几个过程:
android.ipr
,android.iml
android.iml
文件提升as启动速度1.工程文件生成
检查out/host/Linux-x86/framework/
目录下是否存在idegen.jar文件,存在则说明你已经编译过该模块,若是没有编译过则须要编译。执行以下命令便可:
source build/envsetup.sh mmm development/tools/idegen/
其中mmm development/tools/idegen/
执行完成后会生成idegen.jar,在已经编译过idegen模块后,执行:
sudo ./development/tools/idegen/idegen.sh
会在源码目录下生成IEDA工程配置文件:android.ipr
,android.iml
文件,文件什么做用呢?
android.ipr
:一般是保存工程相关的设置,好比编译器配置入口相关的libraries等android.iml
:则是主要是描述了modules,好比modules的路径,依赖关系等2.修改android.iml
android源码的体量仍是很大的,若是直接使用Android Studio
导入工程,须要漫长的等待时间,看代码仍是选择source Insight。这就要求咱们有选择的导入模块,选择须要导入。在导入前修改android.iml
文件,经过添加配置的方式告诉AS不导入某些模块,好比如今我不想导入art模块,那么就在android.iml
文件中添加:
<excludeFloder url="file://$MODULE_DIR$"/abi>
格式为:<excludeFloder url="file://$MODULE_DIR$"/模块名>
,这里只保留了framworks和packages模块,将其余模块所有排除了,所以在android.iml
中添加了如下配置:
<excludeFolder url="file://$MODULE_DIR$/.repo" /> <excludeFolder url="file://$MODULE_DIR$/abi" /> <excludeFolder url="file://$MODULE_DIR$/art" /> <excludeFolder url="file://$MODULE_DIR$/bionic" /> <excludeFolder url="file://$MODULE_DIR$/bootable" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/cts" /> <excludeFolder url="file://$MODULE_DIR$/dalvik" /> <excludeFolder url="file://$MODULE_DIR$/developers" /> <excludeFolder url="file://$MODULE_DIR$/development" /> <excludeFolder url="file://$MODULE_DIR$/device" /> <excludeFolder url="file://$MODULE_DIR$/docs" /> <excludeFolder url="file://$MODULE_DIR$/external" /> <excludeFolder url="file://$MODULE_DIR$/hardware" /> <excludeFolder url="file://$MODULE_DIR$/libcore" /> <excludeFolder url="file://$MODULE_DIR$/libnativehelper" /> <excludeFolder url="file://$MODULE_DIR$/ndk" /> <excludeFolder url="file://$MODULE_DIR$/out" /> <excludeFolder url="file://$MODULE_DIR$/pdk" /> <excludeFolder url="file://$MODULE_DIR$/prebuilt" /> <excludeFolder url="file://$MODULE_DIR$/prebuilts" /> <excludeFolder url="file://$MODULE_DIR$/sdk" /> <excludeFolder url="file://$MODULE_DIR$/system" /> <excludeFolder url="file://$MODULE_DIR$/tools" />
3.设置jdk和sdk,导入源码
在完成android.iml
文件的修改后,将源码工程导入至AS,以下图所示:
图3.1 源码工程导入至AS
导入时间会比较长,要耐心等待,若是已经将所有项目导入到AS中,而又想排除一些模块该怎么办呢? 此时能够在Project Scureture的Mobules中进行排除。 为了排除android sdudio的Sdk中依赖的干扰,须要改进当前的jdk和sdk,让代码跳转也在源码的文件中,而不能在依赖于外部的sdk项目,点击Project Structure进入到项目配置页面:
图3.2 设置工程的jdk
选择新建,新建一个jdk,将其classpath标签下的内容通通删除,选择Android API 25 Platform将当前新建的JDK配置给它。
图3.3 jdk配置给Android API 25 Platform
###3.2调试源码 搞定上面以后,如今咱们来看看如何用Android Studio一步一步调试代码。首先使用emulator命令启动咱们的虚拟机,启动完虚拟机后,运行的是咱们源码编译的最新的系统。接下来打开android studio,打开源码工程,选择Attache debugger to Android process
选项,在弹出的Choose Process框内必须选择Show all processes,不然看不到相关的进程,能够看到系统的不少进程。如system_process、com.android.systemui、com.android.settings等不少进程。
**注意:**在虚拟机或设备运行了咱们编译的系统后,拥有的是root权限,能够查看全部进程,这与咱们前面说的BUILDTYPE为eng相关,方便开发和调试
选择任意一个进程,这里以com.android.settings进程为例,并在包名为com.android.settings的SettingsActivity的onCreate()
函数上打上断点,打开虚拟机的设置,源码会在onCreate()
函数上中止,此时能够对源码进行断点调试。
##参考