源码阅读系列:源码阅读方法

一.前提条件

1.纯熟扎实的语言基础

  如果你学java,却对反射、泛型、注解一直半解,还是不要去读什么框架了,回去把java基础打扎实反而对你自身更有益。

2.UML能力

  在软件工程中,UML在软件的不同生命周期阶段扮演着非常重要的角色,没有好的UML水平,面对大型的项目源码会束手无策。

3.对业务的理解

  如果你要阅读的项目业务性比较强,事先对业务有一定的了解是必须的。

4.设计模式、重构的掌握

  编程语言什么的没什么好说。着重提一个:设计模式由于Android源代码用到各种各样的设计模式,如果不会设计模式,将会大大降低你的阅读理解速度。

5.对编码规范和约定的深刻理解、认识

  意义重大的编码工作,或大型、有组织体制之下的项目,都会采纳一套编码规范、指导原则或约定。目标是增强代码的可靠性、易读性和可维护性。

  懂得这套规则,可能一个类名、一个方法名就可以帮助您了解整个类或方法的功能,或者约定俗成的分隔,都会对你的思路有一定的辅助。

6.知道你的目的是什么

  你读这个代码是为了解决什么问题,不是为了读而读,你觉得它哪些地方设计的优秀?

二.总-功能了解

  了解项目功能(Sample 和文档)以及主要分为几个大块。另外明白你的需求,比如 PullToRefresh 的下拉实现。

  这个代码是为了满足什么需求而设计的,是为了解决什么问题?有何优点,有何缺点?加入让你来设计解决这个问题,你会从哪些方面着手?你会遇到什么问题?你会怎么解决这些问题?然后看代码相互印证学习。

1.大家可以自己先想想如果是自己会怎么去实现这个项目,或许看源码过程中会发现思想碰撞。
2.如果时间比较紧,可以先 Google 一些该项目相关的文档结合代码看看,帮助快速掌握,不过网上分析文章参差不齐,需谨慎。

三.总-总体设计

关键字:层次(分层)、常见模式对应、流程与关系、自上而下、必须找好切入点、模块、组件

  要了解一个系统,最好是采取由上至下的方式。先试着捕捉系统架构性的观念,不要过早钻进细节,因为那通常对于你了解全貌,没有多大的帮助。阅读程式码不需要从第一行读起,我们的目的并不是在于读遍每一段程式码。

  阅读程式码时,多半会采取由上而下,抽丝剥茧的方式。透过记录层层展开的树状结构,程式人可以逐步地建立起对系统的架构观,而且可以依照需要的粒度(粒度) ,决定展开的层次及精致程度。

  重视架构胜于细节,过早钻入细节,对你理解全貌无益。

  从系统的源代码库洞悉系统的架构,不是一件容易做到的事。然而,在识别出重要的架构元素之后,那么,浏览整个系统,了解系统的结构和属性,以及规划增加、修改和重构活动都会变得更为容易。这是因为,一旦提取系统的构架性特征,我们就立即与系统的创建者共享一套语义丰富的词汇。另外,对系统架构的理解,能够帮助我们了解交互的类型、通信模式和代码结构。

1.输入

源码、别人的分析文档

2.工具

○ 画总体图理清逻辑

  根据使用流程来梳理(非常重要),即先看如何调用的,然后根据调用的流程走一走。

  画类图(只画出重点的方法和类即可),可以主次分明,也可以是草图,可以帮助你快速了解内部的代码流程。使用UML里的类图建立静态结构,分析出类与类之间的关系。

  用例图,类图,时序图,随手画的图都可以

  边看代码边画图,当你把所有的代码都能转化成设计图时,你必然对代码足够了解。

  画图,对于复杂逻辑的程序

  整个库分为哪些模块及模块之间的调用关系。如大多数图片缓存会分为 Loader 和 Processer 等模块。

○ 提问

1.系统如何初始化?

对于app来说,对完整 App 来说就是 Manifest 找到入口 Activity,对于工具库从调用接口中判断入口类。然后在 IDE 中一步步深入即可。
PS:一般不错的开源项目规范都比较好,类、函数、变量从名字上就可以了解作用,所以如果需要快速掌握原理的话看觉得是重点的函数即可。

2.与这个系统相接的其他系统(或使用者)有那些,而相接的边界又是什么?

3.系统如何反应各种事件?

4.系统如何处理各种异常及错误?

○ 取经以往架构经验

设计模式
  一般成熟的项目中包含了大量的设计模式和架构思想。

架构模式
  大规模的软件开发工作必须使用一种合适的架构来构造所要创建的系统,控制其复杂性。这个架构一般制定系统的结构,控制的处理方式,以及如何对系统各个组成部分进行模块分解。大型的系统还可能会从框架、设计模式和特定领域的架构中汲取灵感,重用他们架构上的思想。
在更大粒度上,大型系统的代码常分解为对象模块库,可重用组件,甚至独立的集成。你也可以尝试从组件、库、模块的角度来考虑项目的架构。
我们分析的许多系统都遵循一种简单的“主程序和子例程”结构。其他的系统采用更为复杂的架构结构,来组织它们的各个构成子系统。常见的、重要的结构可以归类为少数迥然相异的架构类型:集中式储存库、数据流、面对对象或分层架构。这些架构类型常结合成一个层次结构,用来控制大型系统的复杂性。一个系统可以同时展示多种不同的架构类型,以不同的方式检查同一系统,分析系统的不同部分,或使用不同级别的分解,都有可能发现不同的架构类型。这块可以读一点架构的书籍。
元素封装:组件、模块、命名空间、对象、库。

○ 动手记笔记

  这里正式开始代码分析,分析过程中如果脑子记不住,多动手记下主要类、函数等作用

  对于框架而言,大概看一下API列表,找到重要的、自己感兴趣的API。

○ 项目的组织

  我们可以通过浏览项目的源代码树——包含项目源代码的层次目录结构,来分析一个项目的组织方式。源码树常常能够反映出项目在架构和软件过程上的结构。考虑apache web服务器的源代码树。

  不要被庞大的源代码集合吓到,他们一般比小型的专门项目组织的更出色。尽管看上去非常庞大,但是找到特定工具的源代码依旧轻而易举。

3.输出

总体图示、遇到的问题清单及源码阅读笔记

四.分-详细设计:深入关心的细节

1.输入

源码、别人的分析文档、理清架构的输出

2.工具

○ 定位

  找到需要了解的具体功能实现模块。工具方法也可如上。

  根据关键字快速搜索阅读,自己需要看的部分的代码。

  一旦定位到目标代码,就针对他进行研究分析,忽略不相关的其他部分。这是一种必须要掌握的技能。如果您觉得在原来的上下文中,理解代码很困难,就将它复制到一个临时文件中,删除所有不相关的部分。这个过程的正式名称是切片。

android中,快速定位感兴趣的功能在 find in path / find usages 的基础之上,更多的 command + F 页面内查找,一般基本定位后都难逃我们的掌心,方法其实都很简单。 https://drakeet.me/quickly-locate-the-function-code

○ 修改并运行代码&debug

  有时,您阅读的代码可能来自于对您来说完全陌生的环境(计算机语言、操作系统或API)。熟悉了编程和基本的计算机科学概念后,大多数情况下,您能够通过源代码来了解新环境的基本情况。但要注意从小型的程序开始阅读;不要立即陷入对大型系统的研究中。编译研究的程序并运行它们。这样您就可以得到即时的反馈,了解代码预想的工作方式,同时还可以获得成就感。下一步就是主动的修改代码来检验您对代码的理解是否正确,再次强调,要从小的改动做起,逐渐增大它们的范围。通过积极的介入现实的代码,您能够快速的从中了解到新环境的一些基本情况。当您认为已经掌握了他们之后,要考虑投入一些努力(可能还需要投入一些资金),采取更有组织方式来学习该环境。阅读相关的书籍、文档或手册,或者参加培训课程,这两种方式相互补充。

  另一种积极地阅读现有代码(作为文献)的方式是改进它。与其他文字作品相比,软件代码是活的人工制品,他们总是被不断的改进。如果代码对您或您的团体有价值,请考虑如何来改进它。者可能涉及到使用更好的设计或算法、为某些代码编制文档,或增加功能。开放源码项目中的代码常常没有很好地编制文档,请将您对代码的理解应用到改进文档上。在现有的代码上工作时,请与作者和维护人员进行必要的协调,以避免重复劳动或因此产生厌恶情绪。如果您的更改更为强壮,则考虑申请成为一个并发式版本控制系统的提交者。

  有疑问了运行一下程序,或者修改一下运行,看看结果和你预期的一致不,证明/否定你自己的想法。

  栈帧清晰的帮我们列出了方法的调用流程,是阅读源码非常主要的工具。

○ 代码执行流程分析(结合栈帧分析更佳)

  一般阅读非自己写的代码都是关注某一部分代码块,比如某一条流程线。如果这份代码让你看的眼花缭乱,条件允许的话最有效地方法就是进行debug调试。一步一步的跟进调试会让你很流畅的根据代码的执行步骤了解代码的逻辑。如果无法debug调试的话,我会从最浅层的代码层入手,理清思路,先不去关注深层的逻辑或者函数,需要使用到得时候在去仔细查看。

  当你在阅读一段程式码时,或许可以试着转换自己的立场,从旁观者的角度转换成为写作者的心态,揣摩原作者的心理及处境。当你试着设身处地站在他的立场,透过他的思考方式来阅读,追踪他所写下的程式码,将会感觉更加流畅。

  对需要的具体的代码进行详细分析。搞清变量的意义和关联关系,搞清实现的逻辑和算法。好的代码,此处最需要注释。

○ 重复阅读,注意注释(尤其是英文注释)加todo阅读

  即使读过的代码,还需要再读读,因为可能遗漏了很多优秀的设计地方,很多地方当时也可能理解失误

  如果你阅读过开源代码,你会看到,一块代码,可能有超过一半的程序代码是法律信息与管理信息,如版权信息、许可信息和程序版本标识符。大型的组织有序的系统通常都会提供此类信息,同时还会归纳具体程序或模块的功能。这是可以直接略过的部分。

○ 提问法分析

  遇到不知为何引入的,问自己,为啥引入,然后通过这个东西看他在哪里使用到了

○ 画局部细节图:三种图足够你看懂任何android代码了

  • ER图:实体类的关系 对于复杂的数据结构。使用抓包工具帮你分析实体的类型和对应关系。

  • 页面布局图:布局结构梳理 看android项目除了类图之外,整个页面的布局结构反而有的时候比类图还要重要 毕竟android属于前端技术

  • 类图.

○ 聚焦,列出重点关注的点,由点深入&切片

  要一下子把所有模仿都理解透,是件比较困难的事情。可以分模块学习

  在具体看一个类的时候,最好首先识别出重要的组成部分。就我们的情况来说,重要组成部分是全局变量和重要的方法。

  要了解一个函数的功用,可以使用下面的策略。

  • 猜。基于函数名
  • 阅读位于函数开始部分的注释
  • 分析如何使用该函数
  • 阅读函数体的代码
  • 查阅外部的程序文档

  切片:在推导程序结构的细节时,一个有价值的概念性工具就是切片。非正式的,您可以将程序片看做能够影响变量的值的一段程序。

○ 化整为零

3.输出

局部图示,问题清单及源码阅读笔记

五.回顾

  1. 解决你在阅读中遇到的问题
  2. 回头复习一下前几个阶段做的事情
  3. 提出改进意见你能否提出改进意见
  4. 设计得优秀的地方
  5. 总结成文档,最好写成博客,推荐一个目录结构。

    项目介绍
    特点
    简单用法(一个demo)
    总体设计
    类图
    流程分析:栈帧+时序图
    核心模块分述
    阅读体会&优缺点&改进意见

六.参考资料

https://www.zhihu.com/question/19759722
《代码阅读方法与实践》

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
在上一篇博文《 LTE下行物理层传输机制(7)-DCI2格式和预编码矩阵的选择 》中已经提到,如果当前UE的传输模式是TM4,且可以执行空分复用(一个PDSCH信道传输2个TB块),那么需要采用DCI2格式来承载控制信息域,使用的预编码矩阵需要参考UE反馈的PMI值,因此属于闭环性质的空分复用。相应的,LTE系统中也有一种开环的空分复用: 如果当前UE的传输模式是TM3,且可以执行空分复用,那么此时PDCCH需要采用DCI2A格式发送,这时的空分复用就属于开环性质的空分复用,不需要参考UE反馈的PMI值。
好久没写android的博客,最近在做一个android的项目,里面用到我们经常用的一个控件就是对话框,大家都知道android自带的对话框是很丑的,android5.x之后除外.所以就出现了自定义view,自己定义美观的对话框.好我们就来自定义对话框. 整体思路:定义一个类然后去继承Dialog类,然后重写相应的构造器方法.大家都知道一般的对话框的创建过程都是来一个AlertDialog.Builder对象,然后使用一些set方法来设置标题内容以及设置一些自定义的view和点击的Button以及相应的点
1.android 的UI线程阻超过5秒就会引发ANR(Application not responding)异常,如果等待超过3秒,你就会失去用户。 2.在android中组件的启动线程被称为主线程(也称UI线程),一般不在这线程中进行耗时的工作,所以我们将线程分为两种,分别是main thread和worker thread,当应用程度运行是时,系统默认的启动线程就是主线程,主要用来加载UI,完成和用户间的交互,所有这些都在同一个线程中进行,所以不能在这个线程中进行耗时工作,不能阻塞UI,androi
接触过自定义控件的开发者一看,笑了,立马关了网页。但是…你真的知道怎么绘制居中文本吗? 我不会?开玩笑,不就是: X=控件宽度/2 - 文本宽度/2;Y=控件高度/2 + 文本宽度/2 好吧,那我试一下。 1.自定义控件基本步骤 自定义View的属性 在View的构造方法中获得我们自定义的属性 #重写onMesure # 重写onDraw OK,简单,直接干起来。 1. 自定义View的属性 按照最简单的来,属性有:文本,文本颜色,文本大小。 我们在 /value/attrs.xml 中这么写: ?xml
首先,自定义控件分为三类: 自定义的组合控件 继承View的自定义控件 继承ViewGroup的自定义控件 在这里,我要写的是第二种,也就是继承自View的自定义控件,第一种自定义的组合控件,我已经写过了,可以在我的博客中可以找到 现在来看一下继承View的自定义控件 首先,需要写一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用

WindowManager的分析 - 2016-07-22 18:07:46

一、Window和WindowManager Window:表示一个窗口,从下面Window的源码中可以看出它有且只有一个实现类PhoneWindow。 The only existing implementation of this abstract class is * android.policy.PhoneWindow, which you should instantiate when needing a * Window. WindowManager:它是系统提供我们操作Window的一个接口

MTK6580-Psensor hal层驱动分析 - 2016-07-22 18:07:25

一、HAL 层Sensor 流程         Hal 就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。 Sensor 打开设备时序图:  其中SensorDevice 属于 JNI 层,与 HAL 进行通信的接口 ; 在 JNI 层调用了 HAL 层的 open_sensors() 方法
ART世界探险(2) - 从java byte code说起 Dalvik时代,如果不做JIT的话,只需要了解java字节码和Dalivk的字节码就够了。但是,到了ART时代,我们可能还要至少学习两种新东西:一个是编译后端的IR中间代码。比如,我们假如使用LLVM做为编译后端的话,需要做从dex到LLVM IR的转换工作。这个IR可能还不只一层,比如分中层的MIR和底层的LIR。 最后,我们还得了解机器指令。仅就ARM来说,现在是64位时代了,我们需要了解的就是AArch64和AArch32两种状态下的A

使用AndFix实现Android热修复 - 2016-07-22 18:07:19

AndFix Github: https://github.com/alibaba/AndFix AndFix介绍 AndFix是一个Android App的在线热补丁框架。使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug。AndFix就是 “Android Hot-Fix”的缩写。  就目前来说,AndFix支持Android 2.3到6.0版本,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime。  AndFix 的补丁文件是以 .apatch 结

[置顶] VR学习 - 2016-07-22 18:07:15

VR学习 由于到去的公司从事VR这方面的开发,为了不打无准备之战,因此学习了一下Google的CardBoard VR实现。(仅仅是表皮,只是看Demo但是还是值得花点功夫看看) 效果图 这里的实现效果其实是,使用到了手机的传感器,陀螺仪(具体的往后面看) 学习Demo(再往深研究) 首先我们先看一下Demo中清单文件的权限和Activity的要求。 //请求网络权限 uses-permission android:name = "android.permission.INTERNET" / //手机NF