经过前面漫长的环境搭建、源码同步、编译之后,终于可以真正做点有意思的事情了 — AOSP源码调试。
AOSP源码导入
IDE内存优化
因为源码非常多,所以导入时IDEA/AS会需要大量内存。所以我们需要编辑IDE的VM选项。配置文件为
- IDEA的是
IDEA_HOME/bin/idea.vmoptions
- AS的是
AS_HOME/bin/studio.vmoptions
注意,AS有一个64位版本的配置文件
studio64.vmoptions
最好一并修改了。
找到上面的配置文件,将对应的内容修改为
- 1
即将VM的堆内存最小和最大都设置为748m
。
IDE创建空的SDK
添加空的SDK的原因是AOSP是以项目的形式导入的,而IDEA/AS的项目至少需要一个SDK,否则会报错。而我们使用空SDK就解决了这个错误并且在代码跳转时不会跳转到桌面版的Java SDK中,而是在Android的源代码里。
我们可以创建一个1.7 (No Libraries)
的JDK,然后将它classpath中所有的jar都删掉。
编译idegen
模块,生成IDE项目文件
首先需要编译idegen
模块,命令如下
- 1
这个命令是为了生成
idegen.jar
文件,默认情况下aosp编译并不会生成该文件。
该文件的路径一般在aosp-root/out/host/linux-x86/framework/idegen.jar
。
然后执行下面命令,生成IDE项目文件
- 1
如果你查看该脚本的源码,会发现它做了2件事情:
- 在out
目录下查找,是否已经生成了idegen.jar
文件
- 执行java -cp idegen.sh Main
源码导入
通过IDEA/AS打开AOSP根目录下的android.ipr
文件,然后我们就可以去休息一会儿了。
这个过程非常慢(加上我的电脑也不快),花了大概一个小时才全部导入完毕。
只是第一次比较慢,后边就会快很多。
AOSP源码调试
这部分是重点参考Debugging AOSP Platform code with Android Studio - Part I - Java Debugger这篇文章,所以就没有必要再完全重复了,这里只是简要列出步骤及容易出现的问题。(为了方便被墙的用户,我借用一下原文的截图。)
导入AOSP源码之后,IDE看起来是这个样子的
然后我们打开Debug配置
在菜单栏上依次点击Run -> Edit Configurations -> Remote
,打开如下的页面
创建一个新的Debug配置文件,可以起名为AOSP_Java_Debug
,端口号改为8700
。如下
这时候如果你直接点击Debug,会出现下面的错误提示
上面错误产生的原因是本机的8700端口并没有程序进行监听,而8700则是DDMS的端口。我们可以使用netstat
命令查看端口的使用情况
这时候我们打开Android SDK附带的工具monitor
,使用如下命令
- 1
我是下载安装的Android SDK,你也可以使用AOSP中编译出来的SDK。
不过这是另外一个话题,因为之前默认下载的AOSP里边没有SDK相关的子项目,或者说不全。
这时候,我们再次执行netsh
命令,发现8700端口已被使用。见下图
在monitor中我们可以看到有3列,分别是
- 进程名(以包名显示,比较友好)
- PID(Process ID)
- 端口号(映射端口号/实际端口号)
进程名是启动app_process/zygotte后,设为包名的
PID很有用,我们可以使用GDB(gdb, gdbserver)进行Native调试
端口号里的映射端口号并不是其在emulator中的端口号,而是DDMS创建的一个映射端口,而实际端口号是DDMS对外的端口,默认为8700
现在,我们可以以拨号程序为例,进行调试。打开源码,添加一个断点
然后,我们连接debugger。
点击调试按钮,你会看到提示
- 1
在模拟器上打开要调试的程序,准备触发断点。
你会在monitor/DDMS中看到拨号程序左边有一个绿色的虫子icon,这表示调试程序已经连接。
这是拨号界面
这时,我们按下数字1
,断点会被触发
然后你可以像调试App那样进行操作。如果你单步调试,你会听到持续不断的拨号音,直到你继续/停止运行程序。如果继续运行程序,你会看到1
已经输入。
遇到的问题与解决
导入源代码提示”External file changes sync may be slow”
导入源码后,IDE会提示”External file changes sync may be slow”,此时我们按照其给定的链接操作即可。
- Add the following line to either /etc/sysctl.conf file or a new *.conf file (e.g. idea.conf) under /etc/sysctl.d/ directory:
fs.inotify.max_user_watches = 524288
- Then run this command to apply the change:
sudo sysctl -p --system
- And don’t forget to restart your IDE.
AS代码跳转问题和源代码提示类的成员方法/变量等找不到
代码虽然可以搜索到(Ctrl+N),但是在源代码里边跳转会出现问题。比如你想跳转到Intent,打开的确实反编译的Intent.class文件,这显然不是我们想要的。
原因:在项目的Dependencies中有很多编译生成的jar文件,当跳转时会优先从这些Jar文件中搜索。
解决方法:在上面的Dependencies中,我们只留下<Module Source>
和空的Library就可以了。而有些文章中说的” 接着点击加号的JARs or directories将你源码的frameworks及external和你用到的其他跳转目录添加到依赖中,然后apply即可。”是没有必要的,因为在<Module Source>
中已经包含了源码。
资源R文件无法跳转
虽然经过前边的设置,我们解决了代码跳转的问题(不再跳转至反编译的代码文件了),但是你会在代码窗口的右边栏发现红色的错误提示 - R文件无法找到。(比如Activity
中的com.android.internal.R.attr.state_focused
)。
aosp默认生成的android.ipr
文件并没有将R文件导入到项目中,所以才会出现这个问题。我们可以手动地将R文件添加到项目的依赖库中:
打开项目的Dependencies
,将目录aosp-root/out/target/common/R
以JARs or dictionaries
的方式添加进去,默认会起名为Empty library
,我们可以改一个有意义的名字,如Generated R
。
设定好之后,你很有可能会遇到接下来的问题。
编辑器提示File size exceeds configured limit (2560000). Code insight features not available.
IDEA(Android Studio是基于IDEA开发的)默认限制了打开文件的最大尺寸为2500K
,我们可以将其改大一点,以满足实际的需求。
配置文件的地址为AndroidStudio-root/bin/idea.properties
,将下面的这一行
- 1
替换为
- 1
具体的数字根据你自己的需要来定,我这里设定为
5000
,即5000K
。
调试连接失败
错误提示如下
- 1
我刚开始就犯了这个错误。
原因就是在monitor中没有选中要调试的进程,导致debugger无法连接。
解决方法:只要选择要调试的目标进程,就可以连接成功。
其它
关于导入源码时的优化
我的建议是,如果你是十分熟悉AOSP源码的建议,无需进行所谓的优化 - 编辑android.iml文件,添加exclude
。
倒是有必要将Denpendies中的所有Jar文件都删除掉。
另外,AOSP中关于这部分优化是这么说的
Excluding source roots and jars
IDEGen keeps an exclusion list in the “excluded-paths” file. This
file
has one regular expression per line that matches paths (relative to the
project root) that should be excluded from the IDE configuration. We
use Java’s regular expression parser (see java.util.regex.Parser).You can create your own additional exclusion list by creating an
“excluded-paths” file in the project’s root directory. For example, you
might exclude all apps except the Browser in your IDE configuration with
this regular expression: “^packages/apps/(?!Browser)”.
但是我这样试验了一下,并没有发现有什么变化。
我之前的试验应该有错误 - 直接在aosp目录下创建该文件并添加规则。
应该是修改
aosp-root/development/tools/idegen/
下的excluded-paths
文件,在里边添加上你要排除的项目地址。
注意,需要在执行idegen.sh
之前修改好该文件。
使用ps
命令查看进程的关系
前面提到monitor中中间的一列是PID信息,我们可以adb shell
连接emulator,然后执行ps
命令查看现在的进程,如下
我们可以发现进程对应的PID与我们在monitor中看到的是一致的。
另外,我划了两个红色线框。
其中纵向的红色线框是PPID,即父进程的ID。由此我们可以查看出线程之间的树形关系。可以发现所有的App进程的PID的父进程是同一个 - 956
,进而看到PID为956的是进程zygote64
,而它的父进程是1,即init。
参考
- java JVM : Xms Xmx PermSize MaxPermSize 区别
- Debugging AOSP Platform code with Android Studio - Part I - Java Debugger
- Android Platform Debugging: Old School bringup routines - Command line native debugging with gdb, gdbserver and gdbclient
- How to build SDK
- Modify AOSP and build SDK
- 如何使用Android Studio开发/调试Android源码
- Debugging aosp platform code with eclipse
- 使用AndroidStudio调试AOSP源码
- 使用Android Studio导入源码
- [Intellij IDEA]File size exceeds configured limit