Android性能优化典范第5季-优化启动时间

1. 了解APP启动

一个APP的启动等待时间越短,用户留在APP的可能性就越大,反之用户可能等待不耐烦,直接切到其他的APP了。所以加快一个APP的启动速度是非常重要的。Android系统提供两种启动模式,冷启动和温启动,冷启动:用户点击桌面图标->系统显示启动窗口->APP窗口,温启动:用户点击最近启动列表->APP窗口。
冷启动
这里写图片描述

简单描述下Android系统为一个APP的启动策略:首先用户点击launcher里的APP图标,系统进程为做一些事情(然后挂起等待),同时会为APP创建一个专属进程,这两个进程同时做事情,直到APP做完了初始化工作,开始显示APP窗口,下面的动图是按时间顺序描绘的。
这里写图片描述

Application Process是我们开发者能够处理的,所以我们需要对下面3点特别关注:

  • MainThread部分,特别要关注onCreate方法和UI布局的inflate和渲染,当layout层次太深时,会影响到APP的启动
  • Application的onCreate方法,不要把太多工作放在这里做,可以通过延迟加载的方式优化。
  • 目前有部分APP会提供自定义的启动窗口,这里可以做成品牌宣传界面或者是给用户提供一种程序已经启动的视觉效果

在很容易出现问题的地方,我们需要坐下来仔细分析并解决。非常幸运,Android提供了一些工具来帮助我们。

1.1 DisplayTime

在Android kitkat (4.4)之后,logcat会输出一个Activity的启动时间,如果发现时间太长,可以针对性分析和解决。
这里写图片描述

1.2 reportFullyDrawn方法[api 18 开始增加]

reportFullyDrawn,在Logcat中的Displayed Time输出语句仅仅适用于非延迟加载的情况,且追踪应用从进程启动到第一次显示的时间大多数情况下是有效的。然而现在的开发过程中,往往采用延迟加载的方式在后台异步加载资源和view,更新view层级,而非阻塞绘制。其结果是被初始化完成的Activity可能已经显示了,但它还没有全部加载完资源,这样系统在评估启动时间性能的时候会将它们分开处理
这里写图片描述
reportFullyDrawn方法的意义就是让系统知道Activity完成了异步加载。

1.3 Method Tracing

Display Time和reportFullyDrawn()可以给出一个明确的应用加载时间,方便我们去分析。但是他们没有显示出在整个启动过程中某些部分为什么会慢的具体细节。想要对这一部分深入观察,可以使用Android Studio自带的Tracing工具。
这里写图片描述
Method Tracing使用:https://developer.android.com/studio/profile/am-methodtrace.html

1.4 Systrace

当你添加追踪函数在你的onCreate()方法中,它会增加你的输出日志,以便系统追踪Systrace工具能够正确的发现所有的分段记录并以图表的形式显示出来。
这里写图片描述

Systrace使用:https://developer.android.com/studio/profile/systrace.html

2.优化Activity的创建

用户启动应用,在应用正在加载准备显示之前,会显示开始窗口。
大多数情况下,用户在启动页等待时间过长,问题最有可能在你主要界面的初始化上。比如:设置主题数据,填充view,加载图片,序列化网络读取的内容,写用户的配置等等,所有为了绘制界面的工作 。
这里写图片描述
如果你不关注这里,这里面的每一个小的操作都可能陷入性能陷阱,比如多次的GC事件或者等待填充布局或者网络请求的阻塞,所有事情都会耽搁用户看到界面的时间。

想要使得APP给用户更好的用户体验,就需要使用相关的工具帮助我们发现问题,比如上节提到的Method Tracing工具,它可以以堆栈快照的形式让你直观的发现哪些功能正在使用比别人更多的时间。

如果在onCreate使用的时间超过16-30ms甚至更长,那就需要探索下什么导致了延迟。

通过嵌入trace命令来调查
这里写图片描述
从开始到结束这部分将被记录,然后在systrace显示,在那里可以发现更详细的信息。

如果发现onCreate加载时间过长,下面几种通常例子会是可能。

2.1 简化view层次

这里写图片描述
填充界面布局导致的界面加载慢。我们可以看到这样的布局有很多层级,需要更多的时间去填充,也需要加载更多的图片和资源,导致增加加载时间。同样的,你可以做一些事情来扁平化层级,减少复杂性,或者砍掉那些不能显示的部分,这些事情马上就会帮你界面减少创建的时间。

2.2 通过异步加载的形式

这里写图片描述
试着去重构你的代码,尽可能的依靠延迟加载和异步线程。通常来说,在onCreate()中对上述所有的东西进行初始化会造成界面的卡顿。这些事情中的一些需要在onCreate()中去做,比如初始化布局中的view对象,而另外的一些可以延迟到后面,比如当真正需要显示图片的时候,我们再去采用异步的方式加载并初始化它。上图可以通过下图来拆解实现
这里写图片描述

3.优化application对象的onCreate

比如下图是我们的application的onCreate方法
图1.1
这里我们需要注意:APP的其他启动工作(比如activity,service)都必须要等待Application的onCreate结束才能开始。

通过情况下,我们会一股脑儿把所有的初始化工作都放在application对象的onCreate方法里执行,如下:
这里写图片描述
更严重的是,比如:阻塞代码,垃圾回收期事件,Disk IO, 网络访问这些非常耗时的操作放在这么重要的application的onCreate方法,会严重影响APP的启动速度。

一般情况,如果application的onCreate方法使用时间超过4-10ms,那么就值得我们探索下什么原因导致的。使用工具还是上节提到的Method Tracing和Systrace.

可以通过懒加载的方式对工作进行分解,如下图:

这里写图片描述
通过把一些耗时任务和可以延迟的任务分解到其他的地方,application对象的初始化工作得到了简化,所以启动速度也将得到提高。

4.正面使用Activity的theme

正确使用主题,可以获得很好的品牌宣传体验。但是错误的使用,会适得其反。

再次重复下APP的冷启动过程
这里写图片描述

如果APP启动速度非常快,那么预览窗口仅仅会显示为一个快速闪动。然后APP就显示出来了。但是如果APP启动是一个缓慢的过程,那么预览窗口就会保留显示相当长的时间,得到启动结束,这会让用户焦虑,从而退出应用。APP启动速度太慢,更严重的会导致ANR。

现在一些开发者为了掩盖这种慢启动体验,通过设置启动窗口主题的方式来替换系统默认的启动窗口,如下图中间
这里写图片描述
这样好像解决问题,但实际则不然,在这种场景,屏幕会看起来更舒服,但并不会减少等待时间或者阻止ANR窗口弹出来,只是在表面上掩盖了问题。也有些APP会通过android:windowDisablePreview禁用预览窗口的方式,就会产生下图的问题
这里写图片描述
中间没有预览窗口,仍然停留在Home,得到APP启动结束,突然弹出APP窗口。这又会出现另外一个问题:在APP启动结束之前,屏幕会冻结住,不能接受任何用户操作。

需要强调的是,用户能够感知的启动时间非常重要,不是什么可以胡搞的事情:smile:, 如果你真的想在启动闪图中加入自己的品牌,就需要使用正确的方式,推荐遵守MaterialDesign设计选择,为启动Activity启动自定义图片,如下图:
这里写图片描述
也可以在Java代码中设置
这里写图片描述

5.减少apk大小

所有人都知道apk大小是用户选择是否下载的一个重要因素,没有人愿意等待30分钟在2G网络或其他情况下载一个巨大的APP。减少apk大小无非包括两个方面:减小resources和减少代码量,而resources往往占据apk更大一部分。

5.1 减少Resources大小

5.1.1 去除无用图片

这里写图片描述

shrinkResources true会移除没有用到的资源,特别是当使用到了一些第三方library时,我们往往会忘记删除无用的资源文件,它可以帮助我们自动删除。

5.1.2 正确提供支持分辨率的图片

APP可以适配所有的分辨率,但是不意味着每个分辨率文件都需要提供一份图片资源拷贝,framework会使用存在的图片为对应的分辨率自动scale,如下图,一般情况,提供高分辨率图片,因为自动scale之后不会失真。
这里写图片描述

5.1.3 使用VectorDrawable

使用VectorDrawable,可以在运行时,动态生成每个分辨率需要的图片。
这里写图片描述
如下图,使用VectorDrawable可以显著减少drawable大小,如下图:左边是PNG,右边是VectorDrawable
这里写图片描述

使用VectorDrawable还可以显著减少因为帧动画导致图片资源过大的情况
这里写图片描述

使用VectorDrawable需要注意以下几点:

  • PNG/JPEG是通过硬件进行文理渲染,而VectorDrawable需要先进行xml解析,然后才能进行文理渲染
    这里写图片描述

  • VectorDrawable适用于简单有规则的图片,那些纹理过于复杂的图片不适用,因为不仅仅会过度增加描述文件的复杂度(需要解析)还可能无法获取到想要的渲染效果。
    这里写图片描述

  • VectorDrawable关于Path的描述需要尽量简化,复杂冗余的Path信息不仅对得到想要的图片没有益处,还增加了加载渲染的难度。
    这里写图片描述
5.1.4 避免使用重复的图片

这里写图片描述
如上图,右边的箭头可以通过左边的箭头旋转得到。

上面4点看起来微不足道,但是却可以极大的减少你的APK大小
举个Android Team 的例子,他们通过如下方式
这里写图片描述
APP的drawable大小减少了80%

5.2 减少代码量

  • 通过在build.gradle设置minifyEnabled true来使用proguard来减少代码量
  • 注意因为编译行为额外产生的方法数,例如类似Enum,Protocal Buffer可能导致方法数与类的个数增加。
  • 注意使用的第三方library,它们有可能是为server/pc设计的,里面往往包含了很多在mobile无关的code,虽然proguard可以帮我们移除一些,但不是全部,所以我们应该选择一个使用mobile版本的library.

5.3 APK包拆分

往往情况,APK包含了所有的功能,但是在特定平台下,往往会产生浪,所以可以对APK进行拆分,如下图:APK拆分成了三个小版本。
这里写图片描述
如何拆分APK,可以参考官方教程:configure-apk-splitsmultiple-apks

回顾

首选我们了解了APP的启动过程,

  • Activity的onCreate和Application的onCreate使我们重点需要关注的地方,一般情况Activity的onCreate时间不能超过16-30ms,application的onCreate时间不能超过4-10ms。
  • 可以使用method tracingsystrace帮助我们分析更详细的导致启动速度慢的原因。
  • 正确使用主题,当你需要自己的启动闪图时。
  • 了解了怎么减少apk大小

感谢:Android Performance Patterns

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值