您的当前位置:首页>新品 > 正文

app冷启动速度如何优化?app冷启动速度优化的流程

来源:CSDN 时间:2023-02-06 14:39:02

1. 背景

App的启动速度极大影响到用户体验,启动又分为冷启动、热启动和温启动三种,冷启动是从零创建进程并完成初始化的过程,是三种启动方式里面挑战最大的,而且优化了冷启动速度,也能间接优化其他启动方式,业界一般说的启动速度优化指的就是冷启动速度优化,因此本文亦专指针对app冷启动速度的优化。


(相关资料图)

2.问题分析

首先需要定义问题,相信大家都有这样的体验,如果给你一个工程代码,然后问你该如何优化?至多能看出代码结构上面的问题,比如某几个模块耦合比较严重,不利于维护;存在很多重复代码,代码的工程结构划分不够合理;而对于性能问题,则是看不出要如何优化的,因为性能问题依赖于代码的执行,工程代码总是要依赖于系统的框架才能正常跑起来,比如安卓app代码总要受限于系统提供的组件application、activity、window、service等等,引用的第三方库比如网络框架、缓存框架等,它们运行的快慢对我们来说也是不透明的。所以,单纯的看代码来进行优化是行不通的,需要结合系统框架来画出整个的执行顺序图。   上图是结合app自身业务,简单描述了从手机桌面点击icon图标开始到首页加载的整个流程,同时标注了每个阶段对应的界面,也就是用户可感知的部分。整体流程图按照执行顺序一共分为三个阶段:   1、进程启动:主要是完成各个模块包括第三方sdk的初始化,界面上对应于启动页,此界面可配置,几乎所有的应用都有这个页面。 2、闪屏加载:负责自有广告和第三方广告的加载展现,界面上对应于闪屏页,绝大多数app都有这个界面,一般和自身业务关联不大 3、首页加载:完成自有业务数据的加载展现,界面上对应于app业务的首页,是真正涉及到自身业务的界面   这三个部分在界面上都有体现,哪部分代码执行耗时,就会造成相应的界面展现变慢,切换界面存在突兀、不流畅的情况。   由于启动页是系统为了优化用户体验,允许第三方应用在进程启动时配置的一个界面,不能算是真正的自有界面。首页用户最先看到是卡片数据的展现,因此,为了便于问题的解决我们将整个流程分为两个步骤来优化: 1、从桌面点击到广告页的展现优化2、首页数据展现优化

3.1 从桌面点击到闪屏页的展现优化

如上图所示是这部分的代码执行情况,整个图中间的横轴是时间轴,上部分属于主线程操作,也是用户可见的部分,下面的部分属于子线程操作对用户不可见。   启动页部分:一部分必须要在主线程进行初始化,另一部分则放到了子线程进行初始化。   闪屏页部分:灰度、账号、闪屏等数据的请求处理和闪屏数据的页面展示,而且页面展示部分也已经进行了缓存优化   单独看这两块儿其实都已经进行了优化,直观上可优化的空间很小,此问题是否存在更优解?不得而知,一时陷入了僵局。但是看看手机里其他的app,比如今日头条启动速度真的是很快, 现在回到问题的出发点:我们的目标就是让启动页展示时长越小越好,让闪屏页以最快的速度出现。其实就是将主线程操作尽可能的移到子线程,尽管一个方法执行几十毫秒,但是积少成多想必能优化不少,只能硬着头皮去试验了。   启动页优化启动页展示时间长短主要受进程初始化中主线程各模块的初始化影响,子线程初始化模块是不影响界面加载时长的(前提是子线程的优先级设定为低于主线程,否则还是会影响)。当前简单的将进程初始化分为主线程模块初始化和子线程模块初始化是不够的,可以将划分的粒度进一步细化,比如我们可以充分利用闪屏页展现的过程,尽可能的将一些不必要立即初始化的模块进行延迟初始化,将一些不能一刀切延迟初始化的模块在该模块第一次使用的时候进行初始化。经过这样的划分得到如下结果:   主线程初始化:进行简单的赋值操作如Context、手势密码设置和初始化ARouter,优化获取进程名称实现方法,将原io方式改为               内存读取方式 延迟初始化:主要是一些第三方sdk进行延迟初始化 用时初始化:gio初始化配置、数据库初始化加载、安全校验等使用时初始化避免进程启动抢夺资源 子线程初始化:将数据库、推送、bugly、abtest合并在同一子线程处理,避免开启线程过多,造成cpu拥塞   闪屏页优化闪屏页优化主要是将灰度、账号、闪屏等数据处理从主线程尽量挪到子线程 现在已经对启动页和闪屏页两部分分别进行了最大可能的优化,理论上来说启动速度应该会提上去,通过 adb shell am start -W闪屏页 测试发现启动速度确实提上来了,而且还很明显达到了600-700ms左右,这个结果还是非常明显的,而且也基本达到了预期。但是和头条进行直观对比发现,广告显示的速度还是比头条慢了半拍,头条几乎是瞬间加载。 现在的问题是:进程启动速度上来了,但是广告展示还是慢一点。首先分析当下广告的加载流程:读取磁盘图片到内存然后设置到view进行展示。如何才能加快这个步骤呢?这个步骤本身已经没有什么可简化了,图片必须要经历从磁盘到内存的加载过程。经过一番思索和分析,一个想法浮现出来: 可以将图片的加载提前,不一定非要在广告界面展现的时候才去加载,可以在进程初始化的时候就开始在子线程进行加载,加载完成后将drawable转交给主线程。回调方式采用观察者模式实现,既保证了加载和展现的松散耦合,也能保证时效性。实现方案如下: OnLoadResultListener是观察者接口,如上图所示。   观察者回调处理逻辑如上图所示,整个加载过程只执行一次,等到界面成功注册到预加载服务,并且预加载完成时,就将结果返回给界面展示。   运行代码然后查看加载效果,已经和今日头条的效果差不多了。至此,第一阶段的优化全部结束。基于小米8 lite手机对App新旧正式版通过 adb shell am start -W闪屏页10次测试结果如下,从均值来看大概提升了3.33倍   小结对整体的优化流程总结如下图所示,对比之前的现状图可发现:青绿色部分进程初始化逻辑一分为四,只在用户可见的主线程执行最必要的初始化;其余部分则要么转移到子线程,要么延迟处理或者用时处理。粉色部分闪屏业务逻辑一分为二,一部分提前到了主线程启动页,另一部分转移到不可见的子线程处理。

3.2 首页数据展现优化

常规做法是针对这些数据进行预加载,然后展示即可,但是首页的数据往往和其他模块间存在耦合,在多个界面展现,如果只是修改首页会引入更多问题,比如数据不一致,维护困难,不仅没有降低问题的复杂度,反而将问题的复杂度又提升了一个层级。所以针对这类业务,首先要看目前设计的架构是否合理,是否便于优化,如果不满足,则先要优化架构,然后才能去优化性能,否则得不偿失。   数据管理优化先分析当前这几类数据的业务结构,如下图所示: 从图中可看出各个模块各自为政,单独处理数据,造成了很深的业务竖井,只改变一个地方,很容易造成数据的不一致。为此需要改变原来面向模块划分业务的方式,改为面向数据服务的方式,打破业务竖井,整个工程同类型数据只维护一份,为各个业务所共享,各个业务模块则变得简单、轻量化,只局限于ui部分。优化后的效果如下图:   限于篇幅,最终实现的代码不再展示,将最终的类图框架展示如下,该框架有以下特点: 1、易用性好,该框架以工厂模式的方式提供给外部使用,入口统一; 2、可读性强,采用状态模式完成缓存、网络、加载中三种不同的状态下的数据处理逻辑,避免重复请求; 3、扩展性好,对于新的业务数据只需要继承自DataProvider即可; 4、可维护成本低,将数据和界面完全解耦,只需要很小的修改就能优化性能、更改业务规则,事半功倍。   首页加载优化正是有了前面重构的基础,所以这部分优化变得非常简单,只需要两步:1、将数据初始化放在进程启动的子线程中去做预加载处理;2、然后在首页直接做数据获取操作即可。就能让首页的直接显示缓存数据,而不是重新发起数据加载

4 优化的一般流程

对app优化的一般流程进行总结如下

4.1 弄清现状

严格按照实际的执行流程画出执行流程,特别注意需要分为用户可见和不可见两部分进行区分,这样才能站在用户的角度来指导后续优化的取舍。

4.2 架构先行

对业务模块进行优化前,一定要分析是否要重构。因为让正确的代码更快比让快速的代码正确要容易的多,否则的话,只会将问题进一步复杂化。

4.3 操作手段:

空间换时间缓存就是典型的应用场景。   充分利用多线程由于ui操作都在主线程,为了用户体验流畅,一定要尽可能的将耗时逻辑放在子线程,子线程一定要设置优先级            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND),不然新创建的线程优先级默认和主线程相等,会和  主线程旗鼓相当的争抢CPU资源。   延迟懒加载站在使用者角度优先加载用户首先体验到的业务,其他业务可以延迟或者使用时再加载。   打破业务间藩篱用户看见的并不是全部的真相,闪屏展示的时候也可以充分利用这段时间去加载其他业务。

5. 写在最后的话

实践的过程是螺旋上升式的,有暂时的迷茫,有百思不得其解的困惑,心情总是处于惊喜—失望—平静的反复切换中。      手机里的所有app都是竞品,都在抢夺用户的时间,竞品的优秀表现省去了我们对做某件事“行不行”、“做不做”的探讨,而是直接去思考“如何行”、“如何做”。      根据实践经验和个人理解促成此文,不足之处在所难免,欢迎批评指正。

标签:

最新新闻:

新闻放送
Top