Android Performance

关于 Android 系统流畅性的一些思考

字数统计: 4.5k阅读时长: 15 min
2018/08/13 Share

最近一直想写一些关于 Android 系统流畅度的东西,因为流畅度这个东西,是消费者最直接能体验到的,再加上 Android 一直为人诟病的越用越卡顿,使得大家在提到安卓机的时候,都会有一丝阴影。这也是大部分人买手机首先会考虑 iPhone 的一个原因。

由于 Google 对 Android 持开放态度,各个手机厂商生产不同产品定位的机器,以及各个 Android 应用的质量良莠不齐,导致影响 Android 流畅度的因素非常非常多,并非大家简单的以为是系统没有优化好,很多时候你会发现,不同 SOC 但是相同的系统,体验完全就是两种。

所以我想和大家聊聊影响 Android 系统流畅性的一些原因,后续大家遇到卡顿的问题,也不会单纯把锅甩给系统,或许你卸载一个 App 就解决了呢.

我想从下面几个方面展开聊这个话题:

  1. 硬件层面
  2. 系统层面
  3. 应用层面
  4. 流畅度优化闭环

准备好了,那就开始吧,欢迎你加入讨论

1. 硬件层面

CPU

cpu 是手机硬件里面最核心的一个器件,这也是把 cpu 作为第一个来说的主要原因,cpu 之所以重要,是因为 Android 系统的运行过程中,大部分是跟 cpu 打交道,cpu 的能力强弱,直接决定了这款手机的档次。

手机 cpu 目前主要有高通、华为、三星、联发科四家在做,每家都有高中低档,高端 cpu 的排名大概是 高通>华为>三星>联发科, 具体的排名可以去这里看(仅供参考):http://www.mydrivers.com/zhuanti/tianti/01/

GPU

各个厂商提供的 SOC 里面,通常包含了 cpu 和 gpu ,所以大部分情况下,只要一说 cpu,其 gpu 也是对应确定的,比如高通骁龙845 SOC 带的 gpu 就是 Adreno 630。

gpu 的能力强弱更多的影响的是 gpu 强相关的应用和游戏,比如绝地求生-刺激战场 、 崩坏3 、极品飞车、狂野飙车等。反而王者荣耀这样的游戏更多的是吃 cpu 而不是 gpu。

Ram

随着 Android 版本的更新,以及硬件的更新换代,Android 系统对内存的需求越来越强,目前4G内存的手机基本上已经成了标配,旗舰机器没个 6G 或者 8G 你都不好意思说自己是旗舰。

内存主要影响系统行为,内存越大,系统就越可以以空间换时间:后台可以缓存更多的进程,杀进程不再那么激进;可以根据用户习惯预加载一些文件或者进程;各种虚拟机、hwui、进程的参数可以往宽松里调。反馈到用户那里,就是快。

当然如果后台进程多了又没有管住在后台跑,那么又会很耗电,有点得不偿失,这也是为什么国内的系统都会对进程管理这一块进行魔改。

UFS && EMMC

ufs 和 emmc 都是面向移动端的 flash 标准,我们最长听说的就是 emmc5.1 和 ufs 2.1 ,具体可以参考这篇文章:https://zhuanlan.zhihu.com/p/26431201

对于用户来说,ufs 和 emmc 的差异主要在文件读取速度、视频加载速度、文件拷贝等方面,总之能上ufs就别考虑 emmc。

不过有时候这个也是需要 SOC 支持的,比如高通 660,就不支持 ufs,能不买就别买吧。

屏幕分辨率

我们最常见的屏幕分辨率是 1080P,即 1920*1080. 对用户来说,屏幕分辨率除了会影响视觉感官外,还会在系统某些地方有差异,比如截图、录屏、合成等操作。越高的屏幕分辨率,在这里的耗时就越久,也越耗电。

这也是部分 2K 手机在某些场景下,把分辨率降低到 1080P 去运行的原因。比较失败的一个例子就是当年的魅族 MX4 Pro ,在硬件性能不足以支撑 2K 的情况下,强行上了 2K 屏幕,导致很多情况下,用户反馈又卡又耗电。

电池大小

电池大小决定着续航,也决定着手机设计,手机厂家往往需要在这两者之间找一个平衡,在电池技术没有突破的情况下,就算各家都有快充,还是建议用户在选购手机的时候,尽量选大容量电池的手机,比如 Oppo Find X 或者 Vivo NEX,或者华为 Mate 10.

SoC 平台

SoC的全称叫做:System-on-a-Chip,除了我们之前说的 cpu、gpu,Soc 上还有很多器件,具体可以看这篇文章:https://zhuanlan.zhihu.com/p/37634251
,这里就不展开讲了。

SoC 是整个手机最重要的部分,是一切体验的基础。现在高通、三星、MTK 给手机厂家提供的硬件就是 Soc ,以及其配套的 Android 适配系统。手机厂商拿到这个之后,在其基础上做整机的设计,系统这边会在配套的 Android 适配系统上做移植,也就是把各家系统差异化的东西移植到新系统上。

从目前的高通、三星、MTK 三家的适配系统的质量来看,高通提供的适配系统是功能最完善的,高通在 AOSP 的基础上,加上了高通自己的非常多的优化代码,并提供了完善的参数供手机厂商去配置,总的来说开发起来是很舒服的,本身系统的问题不会太多,加上高通的文档完善,支持速度快,国内那么多手机厂商都在用高通也就不足为奇了。

至于专利费,该给的要给啊。

我们经常会说,如果魅族早点用高通的 Soc,早 TM 上市了。

2. 系统层面

应用的管控策略

大部分 Android 应用开发者对国内的手机厂商恨的咬牙切齿,最大的原因就是国内系统对应用管控这一块进行了大量的魔改,除非你是 QQ 或者微信,否则灭了屏结果都一样。

国内厂商这么做,不是没有原因的,国内应用厂商的全家桶相互唤醒,已经到了一种丧心病狂的地步,牵一发而动全身,一点都不夸张。

我们遇到的很多用户反馈的整机卡顿问题,抓 Trace 和 Log 来看,都是后台有应用在乱跑,或者后台大量的进程常驻,内存根本不够,而这些普通用户根本就不知道怎么去处理。

所以国内厂商一般会在系统里面做限制,以保障用户的基础体验:

  1. 除非必须,一个应用偷偷拉起来另外一个应用的行为是不被允许的
  2. 除非必须,一个应用常驻后台是不被允许的
  3. 除非必须,一个应用在灭屏后在后台乱跑是不被允许的
  4. 除非必须,一个应用在后台长时间占用 cpu 是不被允许的
  5. 除非必须,一个应用弹窗是不被允许的

另外手机厂商会有其他的逻辑清理后台的应用,尽管你是合理存在的。

对进程的严格管控,也导致了国内系统的体验有一定的影响,首当其冲的是通知,如果一个应用没有接入这个手机厂商提供的 push sdk,那么他这辈子别想发通知给用户了,如果接入了手机厂商提供的 sdk(目前大部分应用的普遍做法),由于应用不在后台,用户点击通知要等好久才可以进入到对应的界面,毫无用户体验可言。

内存策略

手机厂商常常会根据手机的内存大小来定制各种不同的策略,比如后台应用的缓存个数、LowMemoryKiller 的阈值、杀进程模块的阈值、显示模块的缓存大小阈值、用户最常用应用的个数等。

很多低端机用户反馈卡顿,我们查看发现,内存是造成卡顿的主要元凶,在低内存的机器上,由于内存不足,系统会频繁杀后台,同时也有频繁的内存->文件,文件->内存 的操作,Trace 上很多 BlockIO,很多平时执行很快的操作,现在执行要很久,再加上部分进程被杀之后马上重启,重启之后又被杀,cpu 占用很高,此时就会很卡。

随着 Android 系统和应用的更新,只会越来越吃内存,目前4G内存是标配,明年或许 6G 才是标配了,能上 8G 尽量上 8G。

进程调度策略

进程调度策略有时候也会影响用户的流畅性,当应用的渲染链路上,有哪个环节因为某些原因,没有被调度到的时候,很大可能会造成卡顿。

调度不到在 Trace 上的表现是 Runnable,常见的调度不到的情况有:

  1. 同时运行的进程太多,cpu 这边的几个核处理的任务基本都是满的
  2. 进程优先级较低
  3. 调度器过于不灵敏,不能及时响应大任务

另外由于 cpu 引起的卡顿情况还有:

  1. 从大核心掉落到小核心上,小核心处理能力不足,会造成短暂的卡顿
  2. 触发温控或者触发低电量,此时某些系统会限制大核的使用,导致卡顿
  3. 系统锁也是造成卡顿的一大元凶,尤其是 wms 锁和 ams 锁,再加上 binder 通信,relayoutWindow 了解一下?
  4. 核心频率不足,导致函数执行时间过长导致卡顿
  5. 大核心被占用,任务又调度不到小核,导致卡顿

系统调优往往需要针对上面的情况做对应的处理,给用户一个好的用户体验。具体的调优方式,往往跟系统和 Soc 强相关,又涉及到 Kernel 和 功耗,改起来是牵一发而动全身,需要非常谨慎。

渲染线程和主线程

Android 应用的渲染链路上最重要的就是主线程和渲染线程,主线程就是应用启动时创建的 MainThread,对应的也会创建一个 RenderThread(硬件加速默认开启),我们平时比较看重的 GPU Profile 那条线,基本就包含了主线程和渲染线程的各个阶段的执行时间,从 GPU Profile ,就可以很容易看到应用的瓶颈

大部分应用的卡顿都发生在主线程和渲染线程上,比如:

  1. 较长时间的 input 事件处理
  2. 较长时间的动画事件处理,比如 ListView 的新 Item 的生成
  3. 复杂界面的 Measure、 Layout、Draw
  4. 较大 Bitmap 的频繁 upload
  5. 复杂渲染指令的执行

很多编程的不好的实现,都可以在上面几个步骤里面体现出来,这些都可以通过 Systrace 看出来。

当前应用的渲染链路上的一切优先级都应该是最高的,后台的进程不应该对其造成影响,这也是系统优化的核心要素,不过要做到这一点也是比较难的,你很难考虑到所有的情况,比如有的用户的使用环境就是很复杂,而且都是必须的,这时候就不是很好处理。

TripleBuffer

之前有提到 TripleBuffer,这个是 Project Butter 引进的,其中 Vsyncv 和 TripleBuffer 的引进使得 Android 的流畅度上了一个台阶,关于这个可以参考这篇文章 : https://niorgai.github.io/2017/03/21/Android-Draw-System/

对于用户来说这个是透明的,影响的是 GPU Profile 的展示,有时候如果有一条线超过 16 ms 的警戒线,它不一定代表着卡顿,这就是 TrileBuffer 的作用。

后续我会有文章专门讲这个,如何判断是真正的卡顿。

虚拟机 - Art 和 Dalvik

对用户来说,Art 虚拟机相比 Dalvik 虚拟机,最大的提升就是解放了应用的主线程,主线程不再频繁被 GC 线程 Stop ,相应卡顿也减少了很多。

当然 Art 带来的好处不止这一点,Art 随着几个大版本的缝缝补补,已经在很多地方远远超过了 Dalvik,有兴趣的可以自己查一下。

温控 && 低电量

之前提到,一旦触发温控或者低电量,系统会对资源做一定的限制,防止手机无限制过热或者快速关机。这限制就包括

  1. 降低 cpu、gpu 最高频率
  2. 减少可运行的 cpu 的核心数
  3. 杀掉部分后台进程
  4. 关闭部分特效
  5. 限制网络连接

总之,这些限制或多或少会对用户造成影响,最大的影响就是卡顿,这就是很多人会遇到打游戏的时候突然很卡的一个原因。

所以说选购手机的时候,除了要看 Soc,还要看散热是否做的够好,电池是否做的够大。

3. 应用层面

复杂的布局

复杂的布局往往是应用卡顿的最主要的元凶之一,复杂的布局意味着更长的 Measure、Layout、Draw ,这会拖慢主线程的执行速度

ListView、RecyclerView 的新的 Item 在初始化的时候也会有类似的问题,由于此时一般是在滑动,这时候的卡顿感会更明显,用户也更容易察觉,这个从 Trace 上也很容易看出来。

过多的业务逻辑

过多业务逻辑导致的卡顿和响应慢的问题,拿淘宝来举例子最合适不过了,每次你冷启动淘宝的时候,进入主界面马上滑动,总感觉跟吃了屎一样,点按钮点不动,滑界面滑不动,虽然最近的版本有优化,不过你找个低端 Android 机,还是原来的配方。

淘宝在启动的时候,需要动态加载很多东西,导致主界面响应很慢,很多东西要动态加载完成后才可以操作,后台还有大量的 dex2oat 操作,可以说是很忙了。

内存颠簸

频繁申请和释放内存,会导致内存颠簸,从 AS 的内存监视器可以看到这一点,短时间内内存曲线上下跳动非常频繁,这时候你需要检查一下是否代码写的有问题。

慢网络

慢网络指的是用户请求网络耗时很久,这会导致用户在某些界面等待内容需要很久,比如知乎经常会出现这种情况,在用户看来,这就是卡了。

不合理的设计

设计和性能往往不可兼得,需要从两者之间做取舍,设计师的设计往往很炫酷,互相嵌套的动画往往是程序员的噩梦,为了实现这些复杂的效果,程序员往往需要复杂的代码来实现,这对应用的渲染链路的压力是非常大的,而且在不同性能的机器上表现差异很大,高端机用户觉得这个效果棒棒哒,低端机用户卡的要骂娘。

程序员需要有这方面的知识和数据,好与设计师动之以情晓之以理。

不过用户是很挑剔的,现在的用户对性能的要求越来越高,哪怕是低端机用户,所以合适的设计应该考虑到这部分用户、或者针对低端机用户做区分。

代码实现错误

俗称 bug ,很多程序员不喜欢解决性能问题,因为这个东西解决起来,性价比很低,拿我司的程序员来说,解一个性能问题的时间,可以解决十几个界面显示的问题,还未必能真的解决。

不过由于代码实现错误引起的性能问题,必须要最高优先级解决。

4. 流畅度优化闭环

实验室监控 && 模拟用户

开发阶段就用各项数据来做监控和对比,尽量模拟用户的使用环境,尽早暴漏性能问题,早日解决。

用户流畅度数据收集

在用户使用阶段,收集性能数据,针对这些数据做分析,找出用户最多遇到的性能问题,针对性地做优化。需要注意此时不能在用户阶段手机太多的信息,否则会导致观察者效应

至于需要收集的数据,则需要根据相关度模型来做判断,卡顿发生的时候,系统的哪些指标是可能导致卡顿的原因,那么这些指标就是我们收集的数据。

另外用户的场景判断也非常重要,需要知道用户是在哪个场景出现的卡顿,一旦用户的数量到了一定的级别,这个是很容易发现问题的。

针对性地优化

大数据发现问题后,后续就是针对性地进行优化,把用户最常遇到性能问题的场景进行排序,对最常见的场景进行调研和优化。很多时候需要与应用开发厂商进行沟通,

然后需要把这些场景纳入到实验室监控环境里,做到 实验室监控 —> 模拟用户 — > 大数据收集 —> 针对性优化 —> 实验室监控补充. 这样一个闭环。

CATALOG
  1. 1. 1. 硬件层面
    1. 1.1. CPU
    2. 1.2. GPU
    3. 1.3. Ram
    4. 1.4. UFS && EMMC
    5. 1.5. 屏幕分辨率
    6. 1.6. 电池大小
    7. 1.7. SoC 平台
  2. 2. 2. 系统层面
    1. 2.1. 应用的管控策略
    2. 2.2. 内存策略
    3. 2.3. 进程调度策略
    4. 2.4. 渲染线程和主线程
    5. 2.5. TripleBuffer
    6. 2.6. 虚拟机 - Art 和 Dalvik
    7. 2.7. 温控 && 低电量
  3. 3. 3. 应用层面
    1. 3.1. 复杂的布局
    2. 3.2. 过多的业务逻辑
    3. 3.3. 内存颠簸
    4. 3.4. 慢网络
    5. 3.5. 不合理的设计
    6. 3.6. 代码实现错误
  4. 4. 4. 流畅度优化闭环
    1. 4.1. 实验室监控 && 模拟用户
    2. 4.2. 用户流畅度数据收集
    3. 4.3. 针对性地优化