一、前言
在开始启动的优化之前,需要明确启动的启动耗时的指标计算规则,在这里我们定义的规则如下:
启动耗时 = HomeActivity.onWindowFoucusChange - Application.onCreate
首页的首帧渲染的时间,减去 Application onCreate 回调的时间,即为应用启动耗时的时间。
二、Application 阶段方案
在 Application 中的耗时是启动优化的大头,各种组件以及业务线在 Application 完成初始化的逻辑。因此,优化的一个整体思路是,组件异步初始化、业务线懒加载等。
1、组件异步初始化
优化启动耗时的本质上是优化主线程的耗时,因此可以把一些耗时的组件进行异步初始化。
2、延迟业务线初始化时机
在之前的启动框架中,如何完成对业务线的初始化呢?方案其实很简单:提供一个接口层,定义一个 ApplicationDelegate 的类,在该类中包含了 Application 的各个方法的回调时机,各个业务线只需要实现这个接口就行。主端在 Application 中通过 SPI 的方式获取到各个业务线的接口实现,从而完成业务线的初始化。
现在需要怎么做呢?既然是延迟业务线的初始化时机,那么就不应该在应用启动的时候完成业务初始化。应该把这个初始化的时机,交给业务自己,由业务自己完成业务逻辑的初始化。
3、ContentProvider 的优化
ContentProvider 是 Android 的四大组件之一,只有它是在 Android 应用启动的时候完成初始化的。在 Application attachContext 的回调之后,系统会循环遍历所有注册的 ContentProvider,并完成初始化。
ContentProvider 相对来说是一个比较重量级的组件,一个空的 ContentProvider,执行的耗时就达到了 2ms 左右。
但是得益于能够在 Application 的时候就完成初始化,业务大多都喜欢搞一个自己 ContentProvider,便于完成自己业务初始化以及获取 Application Context。
针对的 ContentProvider 的优化措施是,缩减 ContentProvider 的数量。如何缩减呢:
- 删除无用的 ContentProvider
- 合并 ContentProvider
- ContentProvider 的耗时操作延迟处理
3、组件初始化依赖
组件异步初始化之后,但是某些个组件的存在依赖关系,即 A 组件的初始化,需要 B 组件先完成初始化,该如何做呢?
我们可以使用 Android 官方提供的 StartUp 库,里面提供了组件的依赖关系。
4、组件初始化回滚机制
在初始进行异步之后,可能会有考虑不到的点,从而导致线上问题的发生。为了最大程度避免线上问题的发生,减少损失,可以通过开关进行控制。
控制的粒度可精确到某个组件,从而避免影响到整个启动需求的上线。
三、Activity 阶段方案
1、Splash 耗时优化
这一块的优化可以从两个方面来讲:
一方面,合并 Splash 页面和 Home 页面,降低 Activity 切换的耗时。
另一方面,缓存 Splash 接口,并行预加载首页的接口。
在我们进入首页之前,我们会在 Splash 页面先请求一个接口,只有在接口请求完成之后,才会进入首页,完成首页接口请求已经页面展示。
如果接口请求失败,则会卡在 Splash 页面。因果埋点数据发现,大部分情况下,用户在 Splash 的接口返回的数据都是相同的,于是在这里我们可以缓存 Splash 的接口。
另外,如果 Splash 接口没有缓存,我们还有并行请求首页的接口。这样才进入首页之后,拿到数据就可以直接渲染首页的页面了。
2、View 层级优化
减少 View 的嵌套层级,Android 在解析 XML 的时候创建 View 的时候,是通过反射以及递归实现的,嵌套的层级越深,渲染所消耗的时间就越多。
还有一种方案,通过 X2C 的方式,在编译期间把 XML 转化成代码,能减少解析的耗时。但是这种方案可能存在一定的风险,故我们没有采用。
五、防劣化方案
对于防劣化的方案,我们在线上打一些埋点,然后基于埋点,建立线上的看板,并创建监控报表。