免费专栏推荐:小米 MIUI 系统工程师 的《从源码角度看 Android》

1,470 阅读8分钟
原文链接: mp.weixin.qq.com

今天推荐的是小米MIUI系统工程师 YogiAi 开设的《从源码角度看 Android》免费专栏

关于专栏介绍,作者是这么写的:

从源码的角度看 Android,关注本专栏,你将可以同我一起遨游其乐无穷的源码世界。阅读源码的重要性我就不多言了,大家都懂。不过你可以想象有朝一日,通过自己对AOSP源码的理解,让自己口袋中的手机运行着自己修改编译的系统,还是蛮激动的吧 :-)

以下为目前发布的从《源码角度看 Android》 的九个文章标题和简介,作者后续还将会不断更新中(订阅方式看文章末尾):


1、Android SharedPreferences 源码视角

SharedPreferences是Android里最常用的数据持久化方式,其原理是在shared_prefs目录下读写xml文件来存取数据的。关于这块网上的文章很多,我就不重述了。简单列举下我自己画的时序图和一些我的使用建议,最后是容易发生ANR的总结。

2、从源码角度看 traces.txt 是如何生成的

traces.txt 位于安卓系统下/data/anr目录下,当系统中有应用出现ANR时,framework会在弹出Dialog的同时dump出当前各线程的堆栈情况,方便开发者分析出ANR的root cause。

ANR是Application Not Responsing 的简称,简而言之,就是安卓系统内置提示用户应用界面没有反应的机制,是用来避免应用界面一直卡顿,增加系统用户友好度的一种方式。

造成ANR的原因很多,都是因为在主线程执行任务太久阻塞了界面更新导致的,主要有以下几类:

  1. Broadcast Timeout: 前台广播执行超过10s, 后台广播执行超过60s (要注意的是,只有串行的广播才有超时机制,并行的广播并不会超时,也就是说,如果广播是动态注册的,直接调用sendBroadcast触发,如果主线程Looper中排在后面的Message不会触发超时机制,那么即时这个广播是前台广播,系统也永远不会弹出框提示用户超时了)

  2. Service Timeout: 前台服务之星超过20s, 后台服务之星超过200s

  3. Provider Timeout: 内容提供者,publish超过10s

  4. Input Timeout: 按键触摸事件分发超过5s

能够造成ANR的前提是任务是在主线程上执行的,执行什么样的任务主要有以下几点:

  1. 执行耗时任务过久,如文件读取或存储,网络访问获取文件太耗时

  2. 线程被阻塞过久,或者干脆出现了死锁

  3. 线程饥饿,如Binder线程总共16个,Binder主线程占有一个,剩余的15个工作线程都被占满

  4. CPU饥饿,负载值过大,虽然代码正常额但任务一直没有来得及执行

那么回到traces.txt文件,它到底包含了什么信息可以帮助开发者找到ANR问题的根源呢。

在这篇文章,我先从一份traces.txt的日志实例开始解析,然后再通过追踪源码来解释traces.txt是如何生成的。

3、从源码层解析 ContentService 如何实现数据变化监听

ContentService 是 ContentResolver 的服务端,运行在 system_server 进程,为所有应用提供访问 ContentProvider 数据接口。同时 ContentService 也提供监听数据变化的接口,这篇文章将从源码的角度去解析registerContentObserver 和 unregisterContentObserver 的流程。

4、从源码角度看各种 Context

做应用开发的对Context的熟悉度应该是仅次于Activity和Service的。Context,英文名上下文场景,代表着对当前运行场景下的各种信息的一种封装。例如,需要调用四大组件进行工作都要调用到Context,同时,通过Context也可以获取到Resource, Display, Theme, AssetManager, WallPaperManager这些资源对象。

5、从源码角度看 AMS.startProcessLocked

众所周知,Android 系统是基于 Linux 内核的移动操作系统。而 Linux 又是通过 fork 来复制进程,复制的时候只是创建唯一识别符等轻量操作,真正对于资源的使用是借助了写时复制的机制(copy-on-write)。进程与线程的概念在 Linux 的世界中只有资源拥有的差别,本质上它们的内核实现都使用了 task_struct 这同一个结构体,都拥有着各自的 PID, PPID。

在 Android 自成的上层 framework 世界中,进程的概念被层层的封装后已经很模糊了,基本开发者完成开发过程只需熟悉Activity, BroadcastReceiver, Service等四大组件的作用与使用场景,再加上网络访问、数据存储、UI 绘制等等组合而成的业务逻辑即可完成一个很不错的应用。

然而研究 Android 的进程启动时机与实现原理对于进阶学习还是大有裨益的,不仅能够学到进程线程的内功知识,也能够学到设计大师的封装奥妙。Android 应用进程的创建是通过 fork Zygote 进程来实现的,所以所有的应用进程的 PPID 都是 Zygote 的 PID。复制 Zygote 的实例后会得到一个虚拟机实例,除此之外,新建的进程还会获取到一个消息循环、 Binder 的进程通信池以及一个 Binder 主线程。

在这一篇文章中,我将详细解析Android 进程的创建过程。

6、从源码角度看 Binder.linkToDeath

Android系统当中的Binder消息传递无处不在,从运行一个新应用到发送一个常见的TIME PICK广播,再到注册一个ContentObserver去监听短信数据的变化,这些功能都需要使用到Binder通信。正如"凡人必有一死",system_server进程虽然只要手机保持开机状态就会存在,但是普通应用无论优先级多么高,当系统内存匮乏、用户手动杀死应用进程又或者是应用出现不能解决的BUG直接Force Close了。这时,作为进程通信服务端的应用既然死亡了,那么对应的客户端相应的服务端之前保存下来的数据就没有必要保存了。否则的话,应用生老病死之间,如果手机一直不关机,system_server一直在存活期间也不清除死亡进程的遗留信息,那么这样的手机系统使用起来会造成内存泄露,系统资源会慢慢被耗尽直至用户能察觉到的系统卡顿出现。

所以Binder进程通信必然需要一种死亡回调的机制,当通信服务端死亡后可以通知客户端们来进行相关的清理工作。Android已经实现好了一套DeathRecipient机制,客户端只要实现死亡回调binderDied方法并且调用linkToDeath方法,当服务端死亡时binderDied将会被调用。

7、从源码角度看 CPU 相关日志

安卓系统中,普通开发者常常遇到的是ANR(Application Not Responding)问题,即应用主线程没有相应。根本的原因在于安卓框架特殊设定,将专门做UI相关的、用户能够敏锐察觉到的操作放在了一个专门的线程中,即主线程。一旦这个线程在规定的时间内没有完成某个任务,如广播onReceive,Activity跳转,或者bindApplication,那么框架就会产生所谓的ANR,弹出对话框,让用户选择继续等待或者杀死没有完成任务的应用进程。

老生常谈的说法是开发者在主线程中执行了耗时操作导致了任务执行时间太久,这样的问题通常很好定位与解决,往往打个bugreport这个ANR的root cause就原形毕露了,再不济我们也能够通过LOG定位到耗时点。

今天我们讲的是另一种常见的情况,这种情况往往是由于CPU硬件设备的落后、底层CPU调控策略不当导致的。这种问题很恼人,明明按照常理绝对不会出现耗时的地方因为它也能够出现ANR和卡顿,给用户带来极其糟糕的体验。

8、从源码角度看广播

几乎每个安卓应用都无可避免的使用到广播。例如监听WIFI的开启状态、时间的获取,甚至是我们最常用的闹钟功能,都是结合着AlarmManager与广播来实现的。理解广播的注册、发送与接收实现源码将使我们更加懂安卓系统,同时,基于对广播的理解,我们也能很快的掌握AMS中其它组件的实现原理。

9、从源码角度看 Activity 生命周期

Activity是几乎所有初级安卓程序员必学的一个知识点。我们都知道安卓最大的特色就是它能够呈现给用户界面,使用户能够与应用程序交互,这其中最重要的基本类就是各个Activity。然而,很多安卓开发者以为自己熟知了Activity的生命周期与launchMode等等知识点,就以为自己理解了它们。然而事实上并不,安卓源码中潜藏着十分复杂的逻辑支撑着Activity生命周期的变化。

本专栏是免费的,如果大家觉得好,希望可以转发本文。关注小专栏平台,然后回复“从源码角度看 Android”或者回复“源码”可以获取本专栏订阅地址。