Android Studio 如何调试 Framework
更新日期:
Android Studio 调试的优点
Android Studio 作为 google android 官方开发工具,调试的优点简单来说有:
- 可以在源码打断点单步调试
- 打断点后可以查看各种变量的函数值,而且还支持动态修改
- 打断点后可以查看函数调用堆栈
- 打断点后可以查看进程线程信息
总之比单纯打 logcat 打印方便很多。
网上有很多使用 Android Studio 调试 android framework 的例子。原理就是把 framework 代码导入 Android Studio,然后通过挂载调试进程来先实现单步断点调试。但是网上都没说清楚如何把 framework 源码导入 Android Studio,这也是最关键的步骤。本文提供一个指导方法让大家能够顺利使用 Android Studio 调试 android framework。
Android Studio 环境搭建
这个参照我这篇文章:离线 gradle 工程模板。目前使用的是 Android Studio 4.0.1 + Gradle 5.1.1。
配置 Gradle 工程
这里我们导入的 gradle 是不需要编译的(也没法编译),所以不是正常的 gradle,需要在我们上面的 app_template 上进行一些改动。我们以导入 framework 中最常用的 system_services 为例,来说明下 gradle 工程如何配置:
gradle 工程文件树结构如下:
|
|
配置 gradle 工程有2个选择:
(1). 在编译服务器配置 gradle 工程:
好处:可以直接使用 Android Studio 编辑代码,不用来回 copy 代码
坏处:提交的时候要注意不要把 gradle 工程文件提交上去了
(2). 在本地配置 gradle 工程:
好处:不用担心 gradle 工程文件影响代码提交
坏处:得从服务器 copy framework 代码到本地,如何代码有变动需要重复 copy 同步代码
如果选择 (2),就需要把 framework/base 下面的代码 copy 到本地((1)就不需要)。这里说个小技巧,copy 的时候先把在服务器建一个目录,然后用 shell cp 命令把 framework/base copy 过去,然后再去把里面一些不需要文件夹删掉(例如 tests 之类的,基本只要保留 java 的就行了),然后再 copy 到本地 window,这样会快很多。
准备好 framwork/base 源码后,按照上面工程的目录结构把工程文件 copy 到 framework/base 的源码对应目录下面,弄好的 gradle 工程目录应该是这样的:
|
|
下面介绍下 gradle 文件怎么写:
settings.gradle
这里 settings.gradle 的配置有点小技巧,因为之前的 module 文件夹都只有一个层级的,所以 settings.gradle 直接写 module 的文件夹名字就行了。但是这些 framework/base 的不同 module 是分别在不同层级的文件夹目录下的,所以为了适应 framework/base 的源码目录结构得这么写:
|
|
然后在 framework/base/core 和 framework/base/services/core 下放置 build.gradle ,相当于是建立了2个 module。
build.gradle
由于我们需要导入源码,不要编译,所以 build.gradle 的脚本可以比较简单:root project 的 build.gradle:
|
|
然后是 module 的 build.gradle:
|
|
导入 Android Studio 进行调试
上面 gradle 工程准备好之后,打开 Android Studio,选 Import project (Gradle, Eclipse ADT, etc.) 导入:
然后等 Android Studio 完成导入工作,点最下面的 Build 标签会看到 CONFIGURE SUCCESSFUL 的信息就表示工程成功导入了:
注意有些时候可能会报一些编译上的错误,只要显示 CONFIGURE SUCCESSFUL 就可以了,不用理会其他错误的,例如像下面这样也是没关系的:
然后 adb 连上板子就可以开始调试了(注意:板子的固件必须是 userdebug 或是 eng 的,user 无法挂载调试):
挂载上之后,可以就可以在源码上打断点了(注意你设备上跑的固件必须要是你导入的源码编译出来的,不然断点的行号会对不上),例如随便在一个休眠的函数打上断点,然后按 power 键操作设备休眠,断点就能让程序停下来:
之后就能单步执行和查看变量信息了。这些都是 Android Studio 的操作,不会的百度一下就行了,这些不介绍这些了。
后续改进
目前来说有2点不足需要改进:
函数局部变量值无法查看
Watches 那里不显示,手动添加会显示: Cannot find local variable ‘xx’ (如上图显示)。大部分函数的局部变量有这个问题,少数的可以显示;类的成员变量可以显示。
我之前调试用的是 userdebug 的,改成 eng 的就可以查看本地变量了。应该是某个编译选项导致的。网上查了一下,apk 的说是把混淆关了就可以了。然而 services.jar 本来就没开启混淆。我尝试了下面方法仍然无法解决:
- 在 services 的 Android.bp 文件加: optimize: {enabled: false,}, 这个是关闭混淆的,然后 “services.jar” 就没配置混淆配置文件,应该是本来就没混淆的。
- 方案的 BoardConfig.mk 把 WITH_DEXPREOPT := false ,关闭 dexopt 预编译优化。
有空可以研究下是哪个编译选项导致的,这样在 userdebug 的时候把这个选项改改就行了。
配置 gradle 工程 gitignore
直接在服务器配置 gradle 工程是比把代码 copy 到本地方便的,但是 gradle 的工程文件会影响代码提交。目前只能在提交的时候自己注意一下。后续可以考虑把 framework 仓库的 gitignore 配置一下,把 gradle 的工程文件排查一下。如果文件标准,都可以把 gradle 文件也上传了。