摘要
本文主要介绍了途牛App启动流程的优化方案,从梳理现有启动流程、明确任务分级,合理调度、网络优化几个方面入手,解决App在低端机型上启动迟缓、卡顿的问题,达到了在大部分机型上秒开的效果,极大地提高了用户体验。
背 景
为满足不同用户的多样性需求,过去两年里途牛旅游的业务不断膨胀。在业务快速增长的同时,也带来了一些副作用,很多操作环节和页面因为承载功能太多,展示速度变慢,用户等待时间变长,性能优化势在必行。
- 案例内测环境:某版本某机型
- 主要现象:
(1)首页加载黑屏
(2)卡顿
(3)UI展示迟缓
- 主要原因:
(1)app初始化环节混乱
(2)没有明确的优先级界定
(3)存在部分阻塞UI线程的任务
本文笔者将重点针对此案例详细讲述途牛App的启动与首页加载优化的实践。
优化思路
1、界定启动流程范围
从点击桌面icon => 首页框架(MainActivity)加载完成作为一个完整的启动流程,包含以下几种场景:
- (1)正常启动:TuniuApplication => Splash(LaunchActivity) => MainFragmentActivity
- (2)消息推送(App进程不存在):TuniuApplication => Splash(LaunchActivity) => MainActivity => 目标页面
- (3)外部呼起:
- App进程存在:Splash(LaunchActivity) => MainFragmentActivity => 目标页面;
- App进程不存在:TuniuApplication =>Splash(LaunchActivity) => MainFragmentActivity => 目标页面
- (4)进程恢复:TuniuApplication => 目标页面
2、明确任务分级
将启动中的所有任务进行梳理和分级,根据级别来调整执行对应任务的时机:
- 一级:Application级别,需阻塞启动的任务,如插件、打点、fresco等基础SDK的初始化;
- 二级:可延迟到首页加载渲染成功之后再执行的任务,比如splash数据拉取,红包展示等;
- 三级:业务懒加载,按需加载,只有在业务使用时才进行初始化的操作。
并且每个级别执行都会有对应的状态,方便app执行对应的任务。
3、整理流程
根据分级定义,将原有业务流程进行了整理并分级,如下:
一 级
根据业务级别的定义,优化点是:
(1)将一些阻塞主线程的非必要任务移动到launch页,从而提高启动速度,笔者将支付Tnpaysdk的初始化、第三方key获取、GA打点初始化等后移;
(2)Launch页面加载splash操作放在2.2s的广告倒计时,此举App启动速度提高了24%左右;
(3)首页启动速度优化方案,进行本地cache化,优先加载缓存,将首页第一屏数据缓存在本地、并将部分与UI展示无关的任务延迟,放入二级任务中,详见二级任务优化点;经过优化之后60%用户达到了秒开的结果。
优化前:
优化后:
二 级
在不影响首页业务加载的前提下,延迟部分任务放入二级优先级的任务中,如文通sdk下载、load Pushtag、hybrid下载、红包、行程玩法小红点、设备指纹信息的获取等等,如下:
三 级
在梳理任务的过程中,发现历史遗留问题:
在首页tab加载的过程中,同时会加载另个4个tab的内容,而其中三个tab都是RN化,涉及到RN bundle的解压等操作,从而影响首页的流畅性,引起卡顿;
解决方法:
(1)笔者将除首页以外的其他4个Tab采取懒加载的方式,等点击了再创建(包括网络请求发送等);
(2)其次插件的加载,如机票、酒店、火车票等,等用户使用时,按需加载即可。
此外,在梳理过程中,UI线程存在锁的操作,文件操作,从而导致UI线程卡顿,甚至部分性能较弱的机子上出现ANR的场景,这些潜在的因素都可能导致首页的卡顿,优化过程需要重点关注和排查。
4、网络优化
主干互联网传输消耗的时间,主要包括三部分:
- DNS查找;
- TCP/TLS握手;
- 数据传输。
本文主要降低DNS查找耗时,而达到优化作用。基本的实现方法是:
将域名发送给服务器并获取该域名的IP地址,存储在本地,每次发起请求时将该IP地址替换掉域名,进行网络访问,其中需要注意的一点是:对于一个IP主机挂载多个Server的情况,在使用IP直连时,Host头需要设置域名。
5、遇到的坑
为了不影响app启动速度而将一部分任务推迟到splash页进行操作,但根据我们梳理的启动流程里,存在未经过splash而直接进入目标页的场景,那么可能会出现某些流程未执行的问题,从而导致业务逻辑错误,甚至crash。
于是需要进所有页面前,确保这部分任务执行,我们通过分析Activity的启动过程,发现Activity是由ActivityThread 通过Instrumentation来启动的,我们是否可以在Instrumentation中做一定的手脚呢?
通过分析代码ActivityThread和Instrumentation发现,Instrumentation有关Activity启动相关的方法大概有:execStartActivity、newActivity等等,这样我们就可以在这些方法中添加代码逻辑进行判断是否执行过LaunchActivity的初始化,以此判断是否需要执行这部分任务。
接着在这个任务执行完成之后,自动跳转到用户实际要跳转的Activity。
总 结
对于App启动和首页启动的优化,如何分析和解决主要分为以下几个步骤:
- 梳理分析现有流程。移除在主线程的多余操作,此过程需要重点分析锁,或耗时的操作。
- 任务分级。对现有流程进行分级,合理并行。
- 本地缓存。首页的数据离线化,优先展示本地缓存数据,等待网络数据返回之后更新缓存并展示。
- 懒加载。对于第一屏无需立刻使用的事物,做到按需加载,等用户点击时去创建需要的资源。
- 网络调优,使用ip直连的方式,去除DNS查找的耗时,从而达到优化网络性能的效果。