专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
开发者全社区  ·  原来如此:很多公司都不招大龄程序员了。。。 ·  昨天  
开发者全社区  ·  AI 公司董事长...十几刀...刺死 ... ·  2 天前  
开发者全社区  ·  防御式编程:程序员降职后启动 ... ·  3 天前  
51好读  ›  专栏  ›  安卓开发精选

如何调试 Android Framework?

安卓开发精选  · 公众号  · android  · 2017-02-07 21:42

正文

(点击 上方公众号 ,可快速关注)

来源:伯乐在线 - weishu

http://android.jobbole.com/83302/

如有好文章投稿,请点击 → 这里了解详情


Linus有一句名言广为人知:Read the fucking source code. 但其实,要深入理解某个软件、框架或者系统的工作原理,仅仅「看」代码是远远不够的。就拿Android Framework来说,整个代码量非常大不说,那些个动辄几万行的类如何去理解?所以我今天要说的就是:


Debug the fucking source code!!


之前分享过一个答案:大家遇到过什么 Android 兼容性问题?,这里面的有一些非常诡异的问题,我相信光靠看代码你是永远定位不出来的。还有我写的一系列 Android插件框架原理 的文章,这里面涉及到大量Android Framework层的知识,有小伙伴会问,这些Framework层的原理,你是如何学习的呢,有诀窍吗?有!那就是调试。


Debug是一项非常非常重要的技能,毋庸多言。今天我就给大家分享一下「调试Android Framework」的经验,一旦掌握这项技能,那么Java层的任何问题都拦不住你了。


概览


其实整个调试过程非常简单:


  • 在你要调试进程的合适位置打上断点

  • 跟踪代码(Step in/out/over等等)


在展开讲述这两方面之前,有必要先简单了解下调试的基础知识。Java平台的调试是有一个规范化的标准的,那就是JPDA(Java Platform Debugger Architecture);通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。 JPDA 主要由三个部分组成:Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP),以及 Java 调试接口(JDI)。


Java程序的调试无非就是通过一个调试器(debugger)获取对应Java虚拟机的信息,上文所述的JDWP就是调试器与虚拟机通信的桥梁。在dalvik虚拟机内部有一个专门的jdwp线程,Android系统的adbd进程通过socket与各个虚拟机的jdwp线程进行通信,外部调试器通过adb工具与adbd通信进而完成与jdwp的通信。我们通常所说的「attach debugger」指的就是这个意思——连接到指定的需要调试的进程。


调试器工作原理


如何在正确的地方下断点


「正确的地方」包含两个含义:首先,调试是以进程为单位进行的,如果你需要调试运行在进程A 中的代码,却把debugger attach到了B进程,那么这个断点压根儿就是牛头不对马嘴;另外呢,比如你想调试Android的多媒体框架,你得知道media相关的类在哪吧,也就是说需要在正确的函数里面下断点。


如何在合适的进程下断点?


如果是调试我们自己写的App,在Android Studio里面非常简单,在Run菜单de最后面有一个attach debugger to android process 的选项,点击之后会出现一个菜单,选择自己需要调试的进程即可;但是,如果需要调试Android Framework层的代码,这样做是达不到目的的——Framework层的代码通常运行在别的进程(比如ActivityManagerService运行在system_server进程),而这些进程通常情况下是不可调试的,也就是说在attach debugger to android process 的那个菜单里面不会有系统的进程,如下图:


普通的无法调试的Android设备


为什么不可调试呢?上文我们简要讲述了调试器的工作原理,我们知道每一个虚拟机有一个jdwp线程,如果这个线程拒绝连接到调试器,你也就没办法对这个进程进行调试了。Android的所有App进程都是通过Zygote进程fork出来的,我们在android.os.Process这个类里面可以看到android进程的启动过程有这么一句:


if (( debugFlags & Zygote . DEBUG_ENABLE_DEBUGGER ) != 0 ) {

argsForZygote . add ( "--enable-debugger" );

}


也就是说,一个进程是否可以调试是由进程启动时候的参数决定的;普通的App进程如果是debug keystore默认是可以调试的,有或者你在AndroidManifest里面指定debuggable为true也是可以调试的。对系统进程,我们只有采取系统级别的手段:让整个系统可以调试——debug版或者编译参数debuggable为1的系统。


解决这个办法很简单:使用模拟器(真机也行,限Nexus系列刷原生Android系统,把系统启动的debuggable参数修改为1),我的Nexus 5 可以调试的进程如下:

可调试任意进程的设备


这样,系统中所有的Android进程都可以调试了;这一点很重要,比如你要分析Activity的启动流程,相当多一部分代码是在ActivityManagerService所在的进程system_server执行的,如果你把断点打在别的进程,就会产生跟丢了的情况。在比如,你要调试ActivityThread的main函数,在main函数里面执行了一句attach,最终调用AMS的attachApplication的时候,代码就通过Binder IPC调用到了AMS的system_server进程。








请到「今天看啥」查看全文