这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会用到。
好了,废话不多说了。直接来吧。。
这是Android 7.0系统源码下载\编译
原文
最近计划着研究下 7.0的系统源码,之前也没做过什么记录,这次正好将学习的内容记录下来,方便以后复习巩固。
既然要学习我们的系统源码,那我们第一步要做的就是下载源码并进行编译了。
硬件环境要求
1. 编译环境
按照官方的说法,编译Android 2.3.x及以上版本的系统源码需要64位的系统运行环境来支持,而编译2.3.x以下的版本则需要32位的系统运行环境。
2. 硬盘空间
官方建议最好预留100G的磁盘空间来下载源码,150G的磁盘空间用来编译源码,如果使用了ccache(一个高速编译缓存工具,可以大幅加快gcc的编译速度),那么则需要更大的空间来支持。
所以尽可能地保证自己的磁盘空间够大吧,之前就因为磁盘空间预留不够导致源码编译过程中空间不足,狠狠地把自己坑了一把。
3. 内存空间
如果你是在虚拟机上跑,官方建议至少需要16G的内存空间,我的机器只有8G的内存空间跑虚拟机,目前跑起来也没太大问题,就是编译源码的过程非常漫长,不知道是否跟内存大小有关。
软件环境要求
1. 操作系统
Android系统的源码的编译支持Linux跟Mac OS两种,一般情况下,Android系统源码都是在Linux Ubuntu系统上进行开发与的,所以如果你准备使用Linux系统来进行源码编译,那一般推荐安装Ubuntu版本的Linux。
下面列出了各Android版本与编译系统版本的对应关系
Linux:
Android 6.0 (Marshmallow) - Android最新版本 | Ubuntu 14.04 (Trusty) |
Android 2.3.x (Gingerbread) - Android 5.x (Lollipop) | Ubuntu 12.04 (Precise) |
Android 1.5 (Cupcake) - Android 2.2.x (Froyo) | Ubuntu 10.04 (Lucid) |
Mac OS
Android 6.0 (Marshmallow) - Android最新版本 | Mac OS v10.10 (Yosemite) or later with Xcode 4.5.2 and Command Line Tools |
Android 5.x (Lollipop) | Mac OS v10.8 (Mountain Lion) with Xcode 4.5.2 and Command Line Tools |
Android 4.1.x-4.3.x (Jelly Bean) - Android 4.4.x (KitKat) | Mac OS v10.6 (Snow Leopard) or Mac OS X v10.7 (Lion) and Xcode 4.2 (Apple’s Developer Tools) |
Android 1.5 (Cupcake) - Android 4.0.x (Ice Cream Sandwich) | Mac OS v10.5 (Leopard) or Mac OS X v10.6 (Snow Leopard) and the Mac OS X v10.5 SDK |
2.JDK 版本要求
不同的Android版本编译也需要对应的JDK环境,这里列出了各版本之间的对应关系
Android目前最新版本 | ||
Android 5.x (Lollipop) - Android 6.0 (Marshmallow) | ||
Android 2.3.x (Gingerbread) - Android 4.4.x (KitKat) | ||
Android 1.5 (Cupcake) - Android 2.2.x (Froyo) |
搭建编译环境
据上面列出的软硬件要求,我们可以根据自己要编译的Android版本以及自己的设备来选择合适的系统及JDK,接下来我们就来说说如何搭建编译环境。
这里我们主要针对Android 7.0的需要的编译环境分别对Linux和Mac OS进行配置:
设置Linux系统编译环境
1.安装JDK
Android 7.0目前需要openJDK 8的JDK环境
Ubuntu 15.04+
如果你的系统是Ubuntu 15.04及以上版本的话,直接运行如下指令即可直接安装:
Ubuntu 14.04
如果你使用的是Ubuntu 14.04版本,现在并没有专门针对14.0.4可用的open jdk8的包,
但是Ubuntu 15.04 OpenJDK 8的包可以在14.0.4上成功地运行,所以我们下载Ubuntu 15.04 OpenJDK 8的安装包来手动安装:
从 上依次下载下面列出的64位的open JDK 8 的.deb安装包
安装.deb包
先运行apt-get指令更新软件列表
sudo apt-get update
接着依次对上面下载的三个deb文件运行如下指令进行安装:
sudo dpkg -i 下载的文件地址
最后运行 apt-get -f 指令修复安装依赖的包
sudo apt-get -f install
更新系统默认使用的JDK版本
如果你的系统安装了多个版本的JDK,可以通过下面的指令执行切换,会弹出可选的JDK版本,根据你的需要选择对应的版本就可以了:
2.安装所需要的工具包
Ubuntu 14.04
我们编译过程中会用到下面的依赖包,执行如下指令统一安装:
3.设置源码编译输出路径
默认情况下,编译好的系统源码会在源码所在目录的out文件夹下,
如果你希望调整输出目录的路径,可以执行下面的指令指定输出目录:
4.设置USB接口访问设备
在linux下,默认情况是不允许普通用户直接通过USB接口来访问设备的.
推荐方法是以根用户身份在 /etc/udev/rules.d/51-android.rules
路径创建文件。
我们可以通过如下指令来实现(注意用你的系统username替换指令中的):
设置Mac OS系统编译环境
Mac OS的文件系统默认情况下保留了大小写实际上却又不区分大小写。
目前的指令无法支持这样的文件系统,会导致一些莫名其妙的错误,所以在Mac OS上编译Android系统源码,我们必须先创建一块区分大小写的磁盘镜像。
创建一块区分大小写的磁盘镜像
这里我们直接通过命令行来创建:
该指令会在系统根目录下生成一个android.dmg或是android.dmg.sparseimage文件,一旦挂载,将被作为支持Android开发所需格式的驱动镜像分区。
如果之后你需要更大的空间,你可以通过下面的指令进行空间调整:
你还可以在 ~/.bash_profile
文件中,添加帮助函数来挂载跟取消挂载:
之后我们就可以通过执行mountAndroid指令来执行挂载镜像,通过umountAndroid指令来取消挂载。
安装JDK
安装工具依赖包
1. 安装xcode命令行工具
$ xcode-select --install
对于老版本的Mac OS系统(10.8或者10.8之前的),我们需要到进行下载安装.
如果你还没有注册成为苹果开发者,你需要先注册一个苹果账号来进行下载.
2. 到 上下载对应Mac OS版本的macports(类似于Linux下的apt-get,用来帮助你安装其他应用程序)
export PATH=/opt/local/bin:$PATH
3. 通过macports安装make, git以及GPG
$ POSIXLY_CORRECT=1 sudo port install gmake libsdl git gnupg
如果使用的是Mac OS X v10.4版本的系统,还需要安装bison:
$ POSIXLY_CORRECT=1 sudo port install bison
优化编译环境(可选)
设置ccache
我们可以自由选择是否开启ccache编译工具。
ccache是一个高速编译缓存工具,它通过将头文件高速缓存到源文件之中而改进了构建性能,因而通过减少每一步编译时添加头文件所需要的时间而提高了C\C++的构建速度。
从编译的全过程来看,不使用ccache的情况下,编译过程中会多次解析相同的头文件,浪费了处理器周期,更重要的是浪费了开发者的时间,因为他们要等待这一过程的完成。在一个团队中,这一影响可能会更为明显,因为团队成员可能会反复编译解析相同的头文件。
所以,一般对于专门用来编译系统的服务器或是大容量的生产环境,这个功能比较有用,它可以加速重新编译的速度。
开启ccache
要开启ccache,在源码树的根路径下执行下面的指令:
缓存的大小一般设置为50G-100G
接着在 .bashrc (或者etc/profile)中添加下面的指令
export USE_CCACHE=1
默认情况下,缓存会存在home根目录的~/.ccache中,但是如果你使用的是NFS或者其他的非本地文件系统,那么你同样需要在.bashrc指定缓存目录地址
在Mac OS的系统中,你需要将linux-x86替换成darwin-x86:
prebuilts/misc/darwin-x86/ccache/ccache -M 50G
当编译的Android系统是4.0.x或者更老的版本,ccache的缓存路径会有所不同
prebuilt/linux-x86/ccache/ccache -M 50G
这个设置会一直存储在CCACHE_DIR中。
在Linux上,你可以通过以下指令开启对ccache的监听:
$ watch -n1 -d prebuilts/misc/linux-x86/ccache/ccache -s
下载源码
编译环境配置好之后,我们就可以开始下载我们的源码了
安装Repo
Repo是google用写的一个脚本工具,Android使用git作为代码管理工具,一个Android系统由N多个git库构成,如果手动进行一个个下载,那简直是一件非常痛苦的事,而repo就是用来对这些git库进行维护管理跟下载的。
通过Repo工具,我们可以轻松地完成Android系统源码的下载。
1.在系统home根路径下创建bin目录并且添加到path路径中:
2.下载repo工具并设置其可执行
初始化Repo客户端
1.创建一个空目录用来存放我们的Android系统源码,名字自己随便定
2.初始化repo仓库
从主干master下载源码,目前最新版本
如果需要下载某个特定版本系统的分支,可以在上述命令后加-b 版本分支号,这里我指定Android 7.0的版本分支
具体的版本分支号可以到这个地址查看(需要翻墙):
3.同步源码到本地
这时执行sync指令便可以自动下载源码到本地了
使用国内镜像下载源码
由于国内网络的问题,上述操作的源码下载需要翻墙才能进行,速度会受到很大影响,几十G的系统源码可能需要花上上周的时间才能下完,
因此我们可以选择国内的镜像进行源码下载:
参照页面上的描述对上面的指令稍作调整便可以了,站点上写得比较详细,这里我们就不作赘述了。
根据网速的不同,一般一天之内能够下载完毕。
对于下载下来的源码,我们并不能直接刷到我们的目标设备上或者是使用模拟器运行,我们必须对源码进行编译生成对应的image二进制镜像文件,
当然你也可以直接从官网下载对应系统版本的镜像文件:
里我们还是自己来编译下源码,熟悉下整个编译过程。
源码编译
首先我们通过命令行进入到源码目录中,我这里目录的名称是aosp
cd aosp
清空输出目录
为了确保我们编译生成的文件不受之前build构建的文件影响,我们在源码目录中执行下面的指令,该指令会清空out输出目录中的所有文件
$ make clobber
设置编译环境
首先我们通过源码build目录中的 envsetup.sh
脚本文件初始化我们的编译环境,执行
$ source build/envsetup.sh
或
$ . build/envsetup.sh
这两个指令的效果是一样的,会初始化一些有用的命令工具
我们后面执行的一些指令必须在初始化 envsetup
之后才能执行
选择编译目标
接着我们通过 lunch
指令来选择我们需要编译的目标
执行lunch指令
$ lunch
会弹出可选目标项:
所有的构建目标由的形式组成:
对应codename
这是官方提供的一份对照表:
这是官方提供的一份对照表:
Pixel XL | marlin | aosp_marlin-userdebug |
Pixel | sailfish | aosp_sailfish-userdebug |
HiKey | hikey | hikey-userdebug |
Nexus 6P | angler | aosp_angler-userdebug |
Nexus 5X | bullhead | aosp_bullhead-userdebug |
Nexus 6 | shamu | aosp_shamu-userdebug |
Nexus Player | fugu | aosp_fugu-userdebug |
Nexus 9 | volantis (flounder) | aosp_flounder-userdebug |
Nexus 5 (GSM/LTE) | hammerhead | aosp_hammerhead-userdebug |
Nexus 7 (Wi-Fi) | razor (flo) | aosp_flo-userdebug |
Nexus 7 (Mobile) | razorg (deb) | aosp_deb-userdebug |
Nexus 10 | mantaray (manta) | full_manta-userdebug |
Nexus 4 | occam (mako) | full_mako-userdebug |
Nexus 7 (Wi-Fi) | nakasi (grouper) | full_grouper-userdebug |
Nexus 7 (Mobile) | nakasig (tilapia) | full_tilapia-userdebug |
Galaxy Nexus (GSM/HSPA+) | yakju (maguro) | full_maguro-userdebug |
Galaxy Nexus (Verizon) | mysid (toro) | aosp_toro-userdebug |
Galaxy Nexus (Experimental) | mysidspr (toroplus) | aosp_toroplus-userdebug |
Motorola Xoom (U.S. Wi-Fi) | wingray | full_wingray-userdebug |
Nexus S | soju (crespo) | full_crespo-userdebug |
Nexus S 4G | sojus (crespo4g) | full_crespo4g-userdebug |
对照表:
user | 有限的访问权限,主要用于发布正式产品,没有root跟调试权限 |
userdebug | 跟user类型差不多,但是多了root跟debug调试权限 |
eng | 拥有各种调试工具的开发版设置,拥有root跟debug权限 |
如果作为开发使用的话,那我们一般都是选 -eng,
这里我自己是准备在模拟器上运行编译的image镜像,并且我电脑的cpu是intel x86的,所以我选择了 6. aosp_x86-eng
我们可以根据自己的需要选择对应的cpu类型。
安装KVM
首先我们检测下自己的cpu是否支持 hardware virtualization(硬件虚拟化)
egrep -c '(vmx|svm)' /proc/cpuinfo
输出的值如果是大于0的,则表明你的cpu支持
对于Ubuntu 10.0.4以上的版本,我们通过下面的指令安装KVM即可
sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
如果在安装KVM的过程中有什么疑问的话,可以访问下面这个网址查找方法:
这样,在编译完目标intel cpu的镜像文件后,我们运行模拟器就会自动进行加速了。
编译源码
好了,一切就绪,我们可以开始编译我们的源码了,
我们在源码路径下通过 make -jN
指令来进行源码编译,这里的N一般建议设置为cpu核心线程数的1-2倍。
$ make -j4
什么是Jack编译工具链(The Jack toolchain)?
我们知道,我们平时编译Android代码的时候会先将Java代码编译成.class文件,最终再转换成.dex文件,如图:
而Jack编译工具链则跳过了编译成.class文件这一过程,直接将Java代码编译成.dex文件
它的优势:
完全开放源码
源码均在AOSP中,合作伙伴可贡献源码加快编译源码
Jack 提供特殊的配置,减少编译时间:pre-dexing, 增量编译和Jack编译服务器.支持代码压缩,混淆,重打包和multidex
不在使用额外单独的包,例如ProGuard。
如果想进一步了解Jack,可以访问(需翻墙),这里就不作太多解释了。
按照官方的说法Jack可以加快编译速度
但实际编译过程中,在我的设备上Jack会占用大量内存,并且拖慢编译速度,还会报错,而且官方文档上写的配置方式对Jack并不起作用。
Jack编译过程中遇到的问题
编译过程中报Out of memory error并中断编译
在执行make指令后,当第一次编译Java代码的时候,Jack会被启用,这个时候经常会卡住,并且一段时间后报错Out of memory error。
按照官方的说法,Jack并行编译的时候占用的资源太大导致内存溢出了
可以通过在 $HOME/.jack
文件中减小 SERVER_NB_COMPILE
的值来减小并行编译数量, SERVER_NB_COMPILE
的值默认为4
但实际情况是Jack没有在根路径下生成 .jack
文件,并且手动创建设置 SERVER_NB_COMPILE
后重启Jack服务也没有效果。
经测试发现make编译过程中当Jack第一次被启用时会在home根路径下生成.jack-server目录
可以通过修改该目录中config.properties文件里的
值来设置并发线程数。
.jack.server.max-service
同时,你也可以设置增加Jack的内存容量来解决这个问题,指令如下
export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096m"
然后进入到输出路径的bin目录下:
cd /home/cjpx00008/aosp/out/host/linux-x86/bin
执行下面的指令重启Jack服务
./jack-admin stop-server./jack-admin start-server
我改了service大小,同时增加了内存,后来编译的过程中没有发生其他问题。
有办法关闭Jack编译吗?
既然Jack有问题,那我们可以关闭Jack编译吗?
目前来说我还没有找到如何在Android 7.0编译的时候关闭Jack,如果有知道的小伙伴欢迎留言告诉我哈,感激不尽!!
运行编译出的image镜像
经过漫长的等待,我们的源码终于编译结束了,是时候来运行编译出的image镜像了。
这时我们只要在源码目录下执行 emulator
指令即可运行模拟器
$ emulator
第一次启动时间可能会有点长,耐心等待即可
运行成功了,是不是有点小激动呢!
$ source build/envsetup.sh$ lunch$ emulator
好啦,到此,我们的源码就编译完毕啦,下一篇我们来聊聊如何使用Android Studio导入Android系统源码,并通过AS进行Java源码调试,以及使用GDB来调试系统Native C\C++源码。
到了这里,就是一长篇文章告一段落了,之后对Java代码调试,通过命令行实现。 (*打个星号)
开始...
我们知道,Android Framework 的代码主要由Java、C\C++等代码组成,因此,对于系统源码的调试,我们这里将其分为了两部分
1. Java 相关代码的调试
2. C\C++ Native 相关代码的调试
一、Java 相关代码的调试
对于 Java 相关代码的调试,这里我们主要使用 Android Studio 开发工具来进行。
导入源码到 Android Studio
要在 Android Studio 中调试源码,那第一步自然是导入系统源码到 Android Studio 中了。
1. 编译 idegen
对于 Android 源码的导入, Google 官方给我们提供了一个很方便的工具idegen
它位于我们所下载的系统源码路径中:
developement/tools/idegen
(这个位置我找了很久,没找到)
引用 README 的一句话
首先打开命令行工具,cd 进入到源码路径下,
执行如下指令:
#初始化命令工具
soruce build/envsetup.sh
#编译 idegen 模块,生成idegen.jar
mmm development/tools/idegen/
#生成针对 Android 开发工具的配置文件
sudo ./development/tools/idegen/idegen.sh
在执行完上述指令后,会在源码路径下生成下面三个文件
android.ipr:工程相关的设置,比如编译器配置、入口,相关的libraries等。
android.iml:描述了modules,比如modules的路径,依赖关系等。
android.iws:包含了一些个人工作区的设置。
2. 导入源码
接下来我们可以开始导入源码了.
如果你是第一次导入源码, Android Studio 可能需要占用大量的内存,我们需要设置下我们的 VM 选项。
Linux 设备的话在 Android Studio 的 bin/studio64.vmoptions 文件中添加-Xms748m -Xmx748m,
如果你使用的是 Mac ,那么在 AS 目录的 Contents/Info.plist 目录中进行添加。
由于 Android 的系统源码非常庞大,一次性导入 Android Studio 的话需要加载非常长的时间
因此,在正式开始导入前,我们可以打开 android.iml 文件根据自己需要调整要加载的源码。
这里 表示不需要加载的目录,我们根据自己的需要使用 标签添加对应的目录地址即可。
接着,选择 File -> open 选中 android.ipr 文件,打开
这时 Android Studio 就会开始加载源码了
在没有添加修改 的情况下,这个加载的时间会比较长,经过一段时间的等待后,代码就加载完毕了,如图:
这里红色的目录代表被 exclude 排除了,代码加载 scan index 的时候会过滤掉该目录。
在加载完源码后,我们也可以在 Project Structure 中的 Module 选项中右键 exclude 来排除不需要加载的源码目录,如图:
3. 配置代码依赖,确保代码跳转正确
为了阅读和调试代码的时候能够保证代码跳转正确,我们需要配置下相关依赖。
首先是 AOSP 源码的跳转,我们通过 File -> Project Structure 打开 Module,然后选中 Dependencies, 保留 JDK 跟 Module Source 项,并添加源码的 external 和 frameworks 依赖,如图:
然后是 SDK 的设置,确保关联对应版本的 SDK 于系统版本一直
开始调试源码
调试前要设置 Project 的 SDK ,File -> Project
下打开 Project Structure,选中 Project 设置对应版本的 SDK,于系统版本一致:
确保 Android Studio 允许 ADB 调试
接着我们参照上一篇文章中讲的方法打开 Android 模拟器
此时点击 Android Studio 工具栏的 attach debugger to Android process 按钮,会打开 Choose Process 窗口,我们根据自己需要调试的代码选择对应的进程:
这里假设我们要调试 Android 自带浏览器的源码,如图,我们在它的入口文件 WebViewBrowserActivity 中的 loadUrlFromUrlBar 方法中打上断点。
点击 WebViewBrowser 打开 app
打开之后,点击 attach to Android process 按钮打开 choose Process,可以看到 webViewBrowser 运行的进程,选中,ok
然后我们在 app 的 url 输入栏输入 网址进行跳转
如图所示我们可以看到,代码成功进入了断点,然后我们就可以随心所欲地调试我们想要的调试的 Java 代码了。
二、Native C\C++ 相关代码调试
对于 Framework Native 代码,我们这里使用 GDB 工具来进行调试。
什么是 GDB 呢?
它是一款 GNU 项目调试工具,它的功能非常强大,可以用来调试 C 、C++、Object-C、Pascal 等语言编写的项目。
对于使用习惯了可视化 IDE 的同学们来说,它最大的缺点可能就是它不支持图形化了
但是 GDB 提供的指令非常灵活,通过指令我们
可以随心所欲地启动程序,
可以根据自己的需要设置断点,
可以查看断点处的变量,代码信息
可以查看程序运行的调用栈
一旦你熟悉了它,你便可以玩得飞起!
一般情况下,使用 gdb 来调试 Android 源码需要在 Android 设备上安装 gbdserver attach 关联我们需要调试的进程,再使用 gdb 指令去连接 gdbserver 进行调试
不过官方给我们提供了 gdbclient 工具,可以让我们方便地进行 gdb 调试。
开始 GDB 调试
这里我们就基于 gdbclient 来进行实际的 gdb 调试演示:
跟上面 Java 调试一样,我们这里还是以系统自带的浏览器为例。
点击启动图标打开浏览器 app:
打开一个命令行终端,cd 进入到系统源码目录(我的源码路径为 aosp),初始化命令工具:
#进入源码路径
cd aosp
#初始化命令工具
source build/envsetup.sh
#选择编译的源码的版本,参考上一篇文章
lunch通过 adb 指令来查找要调试进程的 PID
#通过 shell ps 指令查找相关进程,grep 搜索过滤 webview 进程 adb shell ps -A | grep webview
如图,2157 为系统自带浏览器 app 所在进程的 PID
使用 gdbclient 命令工具启用 gdb 调试 PID 对应进程
# gdbclient <app pid> 可以启用 gdb 调试对应 PID 进程 gdbclient 2157
等待进入 gdb 调试命令界面
使用 GDB b 命令打断点
在 gdb 指令中,我们使用b <代码文件>:行号 来设置断点.
这里我们选择 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp 代码文件的 drawFrame 方法打上断点,位于文件 71 行:
该方法主要用于绘制帧,当浏览器 app 的界面发生变化时会触发该方法。
我们输入设置断点命令:
b frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:71
输入指令后显示 Breakpoint 2 at 0x7f69e9892c11: file frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp, line 71. 说明我们的断点设置成功了。
在命令行输入c 开始监听
c 即 continue,此时界面上出现 Continuing 说明开始监听进程了
我们点开模拟器,随意操作,触发界面变化时,便会进入绘制帧的代码断点了:
如图,显示进入断点,这样代表我们的代码调试成功了。
这里我们只是演示了一个大概流程
gdb 代码的调试需要你对源码有一定的熟悉,知道哪个进程会调用哪个文件方法。
同时,我们还需要熟悉 gdb 的各种命令,这里给大家推荐一篇不错的入门文章,可以快速入门:
这里补充一点,如果你希望在某个进程启动时就监听,可以使用下面的指令关联目录,得到 pid,再通过 gdbclient 来进行调试
adb shell gdbserver :5039 /system/bin/my_test_app
Process my_test_app created; pid = 3460
Listening on port 5039
gdbclient <app pid>
如果你希望通过 Android Studio 来调试 Framework 的 C\C++ 代码的话,也可以参考下面的两篇文章,不过个人觉得这种方法有一定的局限性。
两篇文章先到这里结束,技术文章很多,但是真正使用起来又可以总结出新的结论和技术操作上的更新。
转载出自:https://juejin.im/entry/5934cfd8fe88c20061ccc63e 谢谢提供!