我是怎么阅读 Android 系统源码的?

这是前言
在Android系统源码上摸索4年,说说我的看法:
显然Eclipse不是阅读Android源码的好工具,不流畅,搜索低效,继承性关系/调用关系都无法有效查看。推荐Source Insight,在这个工具帮助下,你才可以驾驭巨大数量的Android 源码,你可以从容在Java,C++,C代码间遨游,你可以很快找到你需要的继承和调用关系。
顺便,现在东家是Linux+Samba+Windows的工作模式,Linux+Samba用于代码的同步/编译/管理,Windows做代码编辑。
你需要先理解下这个图:Application层就是一个个应用程序,很好理解。Framework提供一个java的运行环境以及对功能实现的封装,简单点说,你家装修总要留很多水电之类的接口吧!Runtime/ART是一个java虚拟机,因为Android上层不是java吗,需要再编译一次成为低级一点的语言识别。从Libraries那些名字也可以看出来,这里有很多高端大气库,它是功能实现区,多媒体编解码,浏览器渲染啊,数据库实现啦,很多很多。Kernel部分负责陪硬件大哥玩,你那些功能实现的区域最终都要调硬件吧,Kernel这家伙已经和硬件很熟了,你就直接通过它来和冷冰冰硬件大哥打交道吧!
我是怎么阅读 Android 系统源码的?_第1张图片

好了,上面这些内容很好理解对不对,现在的问题是:当你拿到一份几G的源码,该从哪里开始呢?经过上面的前言的洗礼,你应该能够很好理解下面这部分了
-------------------------------------------------------------------------------------------------------------------
1.宏观上看, Android源码分为功能实现上的纵向,和功能拓展上的横向 。在阅读源码时需要把握好着两个思路。
譬如你需要研究音频系统的实现原理,纵向:你需要从一个音乐的开始播放追踪,一路下来,你发现解码库的调用,共享内存的创建和使用,路由的切换,音频输入设备的开启,音频流的开始。
譬如你要看音频系统包括哪些内容,横向:通过Framework的接口,你会发现,音频系统主要包括:放音,录音,路由切换,音效处理等。

2. Android的功能模块绝大部分是C/S架构
你心里一定需要有这个层级关系,你需要思路清晰地找到Server的位置,它才是你需要攻破的城,上面的libraries是不是很亲切的样子?看完它长成啥样后,然后你才能发现HAL和Kernel一层层地剥离。
很多研究源码的同学兜兜转转,始终在JAVA层上,这是不科学的,要知道libraries才是它的精髓啊。
3. Android的底层是Linux Kernel
在理解1,2后,还是需要对Kernel部分有个简单的理解,起码你要熟悉kernel的基础协议吧!你要能看懂电路图吧!你要熟悉设备的开启和关闭吧!你要熟悉调寄存器了吧!这方面的书太多了,我建议根据实例去阅读,它并不复杂,不需要一本本厚书来铺垫。
在libraries和kernel间,可能还会有个HAL的东东,其实它是对kernel层的封装,方便各个硬件的接口统一。这样,如果我换个硬件,不用跑了长得很复杂的libraries里面改了,kernel调试好了后,改改HAL就好了。
--------------------------------------------------------------------------------------------------------------------

好了,你现在是不是跃跃欲试准备去找个突破口准备进攻了,但是好像每个宝库的入口都挺难找了
我大概在三个月前阅读完Android UI系统的源码,这是Android最复杂的部分,我要简单说下过程。

我需要先找宝库入口,我要研究UI,首先要找什么和UI有亲戚关系吧!
View大神跳出来了,沿着它往下找找看,发现它在贴图在画各种形状,但是它在哪里画呢,马良也要纸吧?
很明显它就是某个宝藏,但是世人只是向我们描述了它有多美,却无人知在哪里?我们需要找一张地图罗。
开发Android的同学逃不掉Activity吧!它有个setcontentview的方法,从这个名字看好像它是把view和activity结合的地方。赶紧看它的实现和被调用。,然后我们就发现了Window,ViewRoot和WindowManager的身影,沿着WM和WMS我们就惊喜会发现了Surface,以及draw的函数,它居然在一个DeCorView上画东西哈。借助Source Insight, UI Java层的横向静态图呼之欲出了。

完成这个静态UML,我觉得我可以开始功能实现上追踪了,这部分主要是C++的代码(这也是我坚定劝阻的放弃Eclipse的原因),我沿着draw函数,看到了各个层级的关系,SurfaceSession的控制和事务处理,SharedBuffer读写控制,彪悍的SurfaceFlinger主宰一切,OpenGL ES的神笔马良。FrameBuffer和FrameBufferDevice的图像输出,LCD设备打开后,开始接收FBD发过来的一帧帧图像,神奇吧。
由于工作需要大量修改framework代码, 在AOSP(Android Open Source Project)源码上花费了不少功夫, Application端和Services端都看和改了不少.
如果只是想看看一些常用类的实现, 在Android包管理器里把源码下载下来, 随便一个IDE配好Source Code的path看就行. 
但如果想深入的了解Android系统, 那么可以看下我的一些简单的总结. 

  • 知识
    • Java
      • Java是AOSP的主要语言之一. 没得说, 必需熟练掌握.
    • 熟练的Android App开发
    • Linux
      • Android基于Linux的, 并且AOSP的推荐编译环境是Ubuntu 12.04. 所以熟练的使用并了解Linux这个系统是必不可少的. 如果你想了解偏底层的代码, 那么必需了解基本的Linux环境下的程序开发. 如果再深入到驱动层, 那么Kernel相关的知识也要具备.
    • Make
      • AOSP使用Make系统进行编译. 了解基本的Makefile编写会让你更清晰了解AOSP这个庞大的项目是如何构建起来的.
    • Git
      • AOSP使用git+repo进行源码管理. 这应该是程序员必备技能吧.
    • C++
      • Android系统的一些性能敏感模块及第三方库是用C++实现的, 比如: Input系统, Chromium项目(WebView的底层实现).

  • 硬件
    • 流畅的国际网络
      • AOSP代码下载需要你拥有一个流畅的国际网络. 如果在下载代码这一步就失去耐心的话, 那你肯定没有耐心去看那乱糟糟的AOSP代码. 另外, 好程序员应该都会需要一个流畅的Google.
    • 一台运行Ubuntu 12.04的PC.
      • 如果只是阅读源码而不做太多修改的话, 其实不需要太高的配置.
    • 一台Nexus设备
      • AOSP项目默认只支持Nexus系列设备. 没有也没关系, 你依然可以读代码. 但如果你想在大牛之路走的更远, 还是改改代码, 然后刷机调试看看吧.
    • 高品质USB线
      • 要刷机时线坏了, 没有更窝心的事儿了.
  • 软件
    • Ubuntu 12.04
      • 官方推荐, 没得选.
    • Oracle Java 1.6
      • 注意不要用OpenJDK. 这是个坑, 官方文档虽然有写, 但还是单独提一下.
      • 安装:
        sudo apt-get install python-software-properties
        sudo add-apt-repository ppa:webupd8team/java
        sudo apt-get update
        sudo apt-get install oracle-java6-installer
        sudo apt-get install oracle-java6-set-default
        

    • Eclipse
      • 估计会有不少人吐槽, 为什么要用这个老古董. 其实原因很简单, 合适. 刚开始搞AOSP时, 为了找到效率最优的工具, 我尝试过Eclipse, IntelliJ IDEA, Vim+Ctags, Sublime Text+Ctags. 最终结果还是Eclipse. 主要优点有:
        • 有语法分析 (快速准确的类, 方法跳转).
        • 支持C++ (IntelliJ的C++支持做的太慢了).
        • 嵌入了DDMS, View Hierarchy等调试工具.
      • 为了提高效率, 花5分钟背下常用快捷键非常非常值得.
      • 调整好你的classpath, 不要导入无用的代码. 因为AOSP项目代码实在是太多了. 当你还不需要看C++代码时, 不要为项目添加C++支持, 建索引过程会让你崩溃.
    • Intellij IDEA
      • 开发App必备. 当你要调试系统的某个功能是, 常常需要迅速写出一个调试用App, 这个时候老旧的Eclipse就不好用了. Itellij IDEA的xml自动补全非常给力.
  • 巨人的肩膀
    • AOSP项目官方: source.android.com/sour
      • 这个一定要先读. 项目介绍, 代码下载, 环境搭建, 刷机方法, Eclipse配置都在这里. 这是一切的基础.
    • Android官方Training: developer.android.com/t
      • 这个其实是给App开发者看的. 但是里面也有不少关于系统机制的介绍, 值得细读.
    • 老罗的Android之旅: blog.csdn.net/luoshengy
      • 此老罗非彼老罗. 罗升阳老师的博客非常有营养, 基本可以作为指引你开始阅读AOSP源码的教程. 你可以按照博客的时间顺序一篇篇挑需要的看.但这个系列的博客有些问题:
        • 早期的博客是基于旧版本的Android;
        • 大量的代码流程追踪. 读文章时你一定要清楚你在看的东西在整个系统处于什么样的位置.
    • Innost的专栏: blog.csdn.net/innost
      • 邓凡平老师也是为Android大牛, 博客同样很有营养. 但是不像罗升阳老师的那么系统. 更多的是一些技术点的深入探讨.
    • Android Issues: code.google.com/p/andro
      • Android官方Issue列表. 我在开发过程中发现过一些奇怪的bug, 最后发现这里基本都有记录. 当然你可以提一些新的, 有没有人改就是另外一回事了.
    • Google: google.com
      • 一定要能流畅的使用这个工具. 大量的相关知识是没有人系统的总结的, 你需要自己搞定.
  • 其它
    • 代码组织
      • AOSP的编译单元不是和git项目一一对应的, 而是和Android.mk文件一一对应的. 善用mmm命令进行模块编译将节省你大量的时间.
    • Binder
      • 这是Android最基础的进程间通讯. 在Application和System services之间大量使用. 你不仅要知道AIDL如何使用, 也要知道如何手写Binder接口. 这对你理解Android的Application和System services如何交互有非常重要的作用. Binder如何实现的倒不必着急看.
    • HAL
      • 除非你对硬件特别感兴趣或者想去方案公司上班, 否则别花太多时间在这一层.
    • CyanogenMod
      • 这是一个基于AOSP的第三方Rom. 从这个项目的wiki里你能学到很多AOSP官方没有告诉你的东西. 比如如何支持Nexus以外的设备.
    • DIA
      • 这是一个Linux下画UML的工具, 能够帮你梳理看过的代码.
    • XDA
      • xda-developers.com/
      • 这里有最新资讯和最有趣的论坛.
    • 做什么事情都要知道从那里开始,读程序也不例外。在c语言里,首先要找到main()函数,然后逐层去阅读,其他的程序无论是vb、delphi都要首先找到程序头,否则你是很难分析清楚程序的层次关系。

      分层次阅读

        在阅读代码的时候不要一头就扎下去,这样往往容易只见树木不见森林,阅读代码比较好的方法有一点象二叉树的广度优先的遍历。在程序主体一般会比较简 单,调用的函数会比较少,根据函数的名字以及层次关系一般可以确定每一个函数的大致用途,将你的理解作为注解写在这些函数的边上。当然很难一次就将全部注 解都写正确,有时候甚至可能是你猜测的结果,不过没有关系这些注解在阅读过程是不断修正的,直到你全部理解了代码为止。一般来说采用逐层阅读的方法可以是 你系统的理解保持在一个正确的方向上。避免一下子扎入到细节的问题上。在分层次阅读的时候要注意一个问题,就是将系统的函数和开发人员编写代码区分开。在 c, c++,java ,delphi中都有自己的系统函数,不要去阅读这些系统函数,除非你要学习他们的编程方法,否则只会浪费你的时间。将系统函数表示出来,注明它们的作用 即可,区分系统函数和自编函数有几个方法,一个是系统函数的编程风格一般会比较好,而自编的函数的编程风格一般比较会比较差。从变量名、行之间的缩进、注 解等方面一般可以分辨出来,另外一个是象ms c6++会在你编程的时候给你生成一大堆文件出来,其中有很多文件是你用不到了,可以根据文件名来区分一下时候是系统函数,最后如果你实在确定不了,那就 用开发系统的帮助系统去查一下函数名,对一下参数等来确定即可。

      重复阅读

        一次就可以将所有的代码都阅读明白的人是没有的。至少我还没有遇到过。反复的去阅读同一段代码有助于得代码的理解。一般来说,在第一次阅读代码的时候 你可以跳过很多一时不明白的代码段,只写一些简单的注解,在以后的重复阅读过程用,你对代码的理解会比上一次理解的更深刻,这样你可以修改那些注解错误的 地方和上一次没有理解的对方。一般来说,对代码阅读3,4次基本可以理解代码的含义和作用。

      运行并修改代码

        如果你的代码是可运行的,那么先让它运行起来,用单步跟踪的方法来阅读代码,会提高你的代码速度。代码通过看中间变量了解代码的含义,而且对 以后的修改会提供很大的帮助

        用自己的代码代替原有代码,看效果,但在之前要保留源代码

        600行的一个函数,阅读起来很困难,编程的人不是一个好的习惯。在阅读这个代码的时候将代码进行修改,变成了14个函数。每一个大约是40-50 行左右.

      ———————————————源码下载网站—————————————————————

      跟你说几个我常用的源码下载网站

      csdn(中文IT社区)它是集新闻、论坛、群组、Blog、文档、下载、读书、Tag、网摘、搜索、.NET、Java、游戏、视频、人才、外包、第二书店、《程序员》等多种项目于一体的大型综合性IT门户网站,源码只是其中的一项,但是很实用 里边有很多大牛。

      DevStore(源码下载)主要是开发者服务平台,汇集国内外众多第三方开发者服务,为开发者提供从设计开发到运营推广一站式的解决方案,源码和服务评测也是亮点,很专业,很实用,这里边聚集的都是开发者和PM,可以看看。

      站长之家(网站源码)针对个人站长,企业网管提供的资讯和源码,包含的语言和类型也比较多。【一个目标】
      阅读代码和阅读文字本质上区别不大,阅读中最重要的观念就是主动阅读,阅读代码也需要主动,比如为实现某功能去修改 framework,或者定位 bug。目标明确才不容易迷失在浩如烟海的代码丛中。即便你只是为了纯粹学习,也拟定类似的一个目标。

      【一个隐喻】
      你是侦探。从大量庞杂的信息中找到犯罪线索。

      【三种透视】
      建筑或者机械制造中,典型的就是几种透视图,代码也应该这样。软件架构或者设计有太多的描述方法(比如UML),我说的透视并不是这些,而是语言表达层面的:What,How & Why. 

      What 是抽象表达(类似学习数学语言,比如当你学会乘法,就不会总是考虑多次加法,以减轻思考负担),比如代码中定义(抽象出)了 Service,你要适应用 Serivce 去看待、分析问题,而不是 Service 里面具体的小组件。

      How 是你掌握了 What 描述的语言之后,希望了解具体的实现,从机器/编程语言/模式等的角度去了解实现的技术和技巧。

      Why 是最终的目标,也是最难的。你懂的Google的SDE为何这样设计和实现 Android。

      三种透视帮助你降维阅读代码的复杂度。

      【四种工具】
      Evernote 或者任何笔记本(不带语法高亮)。为什么?将一个文件源码copy到 EN 中,并设置为浅灰色,表示还没看(懂);在阅读的过程中,将已读(懂)的部分恢复成为黑色,并使用彩色写下自己的阅读笔记或者自己的代码注释。这样,你明确地知道对一份代码的熟悉程度和不熟悉程度,而且阅读目标体现也更为具体:就是将浅灰色读成黑色。

      Vim。轻量级的高端编辑器,一个窗口(带多个tab)阅读一个模块,多个模块分为多个窗口打开。不用复杂的代码静态工具的原因在于:自己摸索出来的代码关系让你记忆和理解更为深刻,并迫使自己高效分析陌生的信息 —— 而这点是阅读代码最重要的能力和结果。

      EA 或者任何方便简单的 UML 可视化工具。不要用文本 UML 工具,这会让你分心,无法聚焦在代码本身。尽管 UML 无法表达代码的全部含义,但它能帮助你发现代码之中潜在的联系。不必追求完美的 UML,记录最重要和当前阅读最需要的信息。就像侦探墙上蛛网般拉出来的罪犯关系图(我知道你看过美剧)。

      Android Studio。写个相关 App 运行下。


好吧,就这样,再往底层我爱莫能助了!

你可能感兴趣的:(我是怎么阅读 Android 系统源码的?)