前言
Android Framework层的代码非常多,在进行frameworks开发&Debug时,如果只能依靠log输出,效率比较低下,而假定可以像普通应用一样进行单步Trace的话,相信效率就会高不少。
本文基于Android Studio,给出了一种可以单步调试Android Framework的简便方法。
如果讲的更通俗一点:
本文提供了一种,“有源码,但是无法通过Android Studio编译”的工程的调试方法
比如原生的那些应用们,Launcher3,SystemUI,Email,XXXXX
比如ActivityManagerService,PackageManagerService,XXXXX
背景知识
attach进程
Android Studio的调试是基于进程的,在Debug前需要先attach到对应的进程。
同样,调试的过程也是基于该进程的,所以我们要搞清楚“运行的代码”到底是在“哪一个进程上的”。
在Android的运行环境中,基本上进程名就是对应AndroidManifest.xml中的包名,所以我们可以通过对应的包名,来找到对应的进程名,进行attach即可。
当然了,对于那些Framework中庞大的XXXXManagerService,通过课外阅读我们可以知道,他们是运行在System_Server进程上的
断点BreakPoint
当Attach到对应的进程之后,就要选择在需要的地方做打断点,对于Android Studio而言,常规的断点通常会有三种显示形式:
- :白板,意味着断点不生效,一般来说都是没有attach到正确的进程
- :叉叉,意味着断点不生效,一般来说是source code跟runtime code不匹配
调试的代码跟实际编译进去的不是同一份,导致java字节码对不上,调试器没办法识别出来。
一般而言出现叉叉是意味着差别太大了 - :勾子,Just Do It,断点会生效,运行到这里会停下来
有时候会出现断点乱跳,也有可能是因为Runtime代码不匹配(但是没有夸张到“叉叉”的情况)
断点类型&调试技巧
请直接参考这篇文章即可,其中有各种断点调试的奇技淫巧供君挑选
游戏开始
构建Fake Project
由于Android Studio的性感Debug被点亮之必要条件是Project建立
这里我们针对frameworks和“有源码但无法编译工程”做两个说明
其实也是一回事….
Frameworks
由于Frameworks是整个android runtime世界的基石,所以对于任意的android app来说,framework的部分都是一样的。
基于这个认识,大体的思路如下:
- 建立Fake Helloworld,目的是点亮debug按钮
- attach到对应的debug进程,比如System_Server
- 找到正确的代码
- 加断点,开始调试
其中3.找到正确的代码是一个问题点,我们就这个问题展开说明。
- 由于要调试的代码在Framework中,在没有配置的情况下没办法直接索引到相关的代码
- 因此第一步是要先下载一份sdk source code
- 下载完以后,由于我们在Target上的代码与Google原生的会有出入,所以需要把source code跟runtime code做一下同步,这部分有两种做法
- 一种是替换掉刚才步骤1中下载下来的source code
- 另外一种是修改Android Studio的引用关系,让它在查询source code的时候直接指定到我们的source code
- 修改:C:\Users\用户名\.AndroidStudio2.2\config\options\jdk.table.xml
需要注意的是,由于我们的fake hellowrld的compilesdk是android n,所以替换的路径也要是android n
- 修改:C:\Users\用户名\.AndroidStudio2.2\config\options\jdk.table.xml
- 因此第一步是要先下载一份sdk source code
同步代码的tips
由于我编译的代码是在VM端,而调试的部分是在本地进行的。
如果把工程的索引直接建立到映射盘符(Samba)的远端服务器,会因为网络的因素造成各种卡顿。
但如果不把索引建到映射的盘符,又会因为每次在VM端修改代码,编译以后要手动同步,非常麻烦。
鉴于这种情况,我选择使用git来帮我完成同步的工作。
- 以VM端的git repository为base,本地通过git clone的方式把代码弄下来
git clone XXXX@110.119.120.114:/home/fucking/source/code/frameworks/base/.git
- VM端的修改每次都做成一个git commit,然后正常编译
- 本地到对应的git repository做git fetch & git rebase or merge or 直接git pull
当然,如果你有更好的办法,也不妨share一下:)
“有源码但无法编译工程”
如果你真的了解了前面的内容,这一块应该也就不难理解了。
对于有“有源码但无法编译工程”,我们可以通过Android Studio的import project来帮助建立一个project。
当然,也可以通过建立fake helloworld,然后把工程的源码们拷贝过去。
之后,就是常规的debug流程了
- attach进程
- 打断点
切记source code要和runtime code一致!
Attach到任意的进程,们
建立完Fake Helloworld,我们欢天喜地的打开attach debugger,结果发现只有一个进程 or 空白。
这个例子我是通过Android Studio导入了“有源码但无法编译工程”的SystemUI
然而,我实际需要调试的ActivityStarter.java中的方法,显然这是Framework中的代码,运行在SystemServer上。
而对应的,我现在打的断点是“白板”,也就是并没有生效,所以我们需要attach到System_Server进程,点击“Show all processes”
当attach成功之后,对应的debug面板就会出现debug client,同时,在attach到正确的进程后,断点也会生效。
写在最后
另外对于Frameworks而言,aosp的工程还提供了一种编译idegen的方式来进行调试。
具体的过程可以参考:生成idegen的调试方法
这个方法我也有尝试过,但由于生成的android.ipr和android.iml实际是整个source code的索引,所以对于在VM端编译,本地端debug的同学来说依旧逃不出网络传输卡顿的魔爪,所以我这边不是很推荐window + VM的同学来玩。
对于使用Ubuntu or Linux的同学来说,倒是可以试试(本地编译本地调试)
PS:QCOM的源码编译idegen会有error,需要修改一些东西才行,具体由于时间久远已经忘记,且看且调了
最后,祝大家调试愉快:)