闲鱼如何在2个月内实现Android启动速度翻倍的?
作者: 海潴,锦逸
随着闲鱼App端更多新功能、新技术的加入,应用冷启动速度越来越慢,这也意味着用户看到有效内容的时间被拉长,对用户体验有着很大的伤害。目前,在内部测试版本中,我们已经将Android在低端机上的冷启动时间从原来的10s降低到了5s内。
闲鱼是如何快速将启动时间减少一半的呢?分为建立标准
、分析现状
、抓大放小
三个步骤。
建立标准
做性能优化不是讨论哲学问题,建立合理的数据衡量标准非常重要。尽管已经有了很多关于如何卡口关键函数、如何判断页面第一帧渲染完成的讨论,但从代码层面进行判断始终与用户的感知无法100%得匹配。如何迅速建立起启动时间的标准?我们借鉴了手淘的方式和标准,利用内部的魔镜平台,使用视频关键帧的方式记录下App图标被点下到首页第一屏渲染完成作为一整个应用冷启动的过程。这与用户看到的启动过程吻合。
对于设备的选择上,我们使用y67这样一台现在看起来相对性能较差的机型作为优化的目标机型。低端机存在CPU能力弱,IO速度慢等问题,而慢代码与IO恰恰是拖慢应用启动最大的原因。定位优化的目标机型可以更加快速得解决common类型的启动问题。
闲鱼现状
我们先使用日志打点的方式来统计启动过程中耗时的大头,以便可以快速得将启动性能提高上去。可以看到图中,进入首页渲染前,common
、interactive
两部分占去了大部分的时间,这是启动器在执行启动任务。而在进入首页后,页面的请求与view的排版占用了大部分的时间。
基于上面的分析,第一阶段我们将”启动任务治理“和”首页渲染加速“作为快速提升启动时间的重点来优化。
启动任务优化
闲鱼的Android端在16年的时候上线了一个基于DAG(有向无环图)的启动器,它将启动任务编排为一个DAG,并使用多核多线程并发的执行任务。上面说到的common
与interactive
属于启动器执行任务的两个阶段,它们都会让主线程等待阶段中的任务全部执行完,所以这两个阶段的任务,我们叫它阻塞型任务
。
目前为止,整个闲鱼Android在启动阶段有77个任务需要执行,其中阻塞型任务有61个,y67上的总执行耗时在8s以上,并发后需要将近2.5s的时间。
对于启动阶段阻塞型任务,最快的优化方式有三点:
- 部分任务延迟执行
- 降低任务本身的耗时
- 拆分大任务
任务拆分与延迟执行
减少阻塞型任务的数量,是加速启动最直接的手段。这里需要根据任务的DAG进行依赖分析,能够无伤被延迟执行的任务最明显的特征就是”没有其他任务依赖于它“。如果任务之间有依赖,则需要根据后续首页对于模块的使用情况来决定是否将整个依赖链上的任务全部延迟。
闲鱼的首页金刚位大部分是weex、web和小程序的入口,另外首页也会用到端智能相关的功能。然而这四个sdk的初始化,普遍都在300-500ms左右,属于比较”硬核“任务。在将这四个任务移动到异步非阻塞阶段后,整个启动降低了500ms(当然要设置最高优先级以保证用户尽量少的等待时间)。
非阻塞任务的触发时机
任务启动的时机就像跟女生表白一样,不是你想启动,启动就启动的。错误的时机大概率造成灾难性得后果。
在我们将几个大任务移动到非阻塞阶段后发现,如果阶段启动的时候首页还没开始渲染或者没有渲染完成,整个首页的渲染会变得非常缓慢,图片的加载也随之变慢。总之就是谁碰到谁倒霉。实测中,非阻塞阶段启动的时机会对首页的渲染产生将近1s左右的波动,使得启动时间不断得在危险的边缘
疯狂试探。
这是由于非阻塞阶段会在进入首页后的第一个queueIdle
回调之后触发。而它的执行占用了多过的系统资源,造成CPU占用、网络请求排队、IO密集等问题。最终导致主线程、渲染变慢的情况。
那么什么时间才是启动非阻塞任务的合适时机呢?既然我们选择首页渲染为最高优先级,非阻塞任务的启动就必须排在后面。于是一咬牙一跺脚——”砍“!
我们让首页在确认view都上屏后,发信号给启动器。启动器这个时候才开始注册queueIdle
回调,并启动一个延迟6s的runnable作为”备份“,防止message queue过忙长时间无法触发非阻塞阶段的任务。
但这里有个矛盾点,首页上几大金刚位都是通往weex、web或者小程序的,如果用户点击这些页面比非阻塞阶段的触发更早,该怎么办呢?当然是原谅触发它啊!
这里我们采用的方式是,当这些功能被触发的时候,需要先去check需要的模块是否已经初始化完成。如果没有的话,check非阻塞阶段是否已经启动。如果已启动,就进入等待,否则强制触发(这个时候首页必然已经渲染完成了),并等待所需要的任务执行完成。
任务耗时治理
要快速治理,需要利用一些成熟的工具。可以先对任务中的每一行代码进行时间统计,筛选出执行时间较长的调用后,使用系统提供的method trace
进行更细粒度的分析。
既然是要快,那么一定是找通用类型的问题下手:
- 对于IO出来的值,尽量做内存缓存,避免多次IO
- 避免产生大的SharedPreference文件,尽可能得将对commit的调用换成apply
- 注意一些异步接口回调的线程,如果是主线程,也需要保证回调后的代码快速执行完
首页启动优化
优化前,闲鱼的首页需要先进行三个排队的网络请求,弹出广告页,接着进行动态模板的渲染与数据绑定,总消耗时间在3.5s以上,这里面还不包括图片上屏的时间。
闲鱼首页部分的启动优化,主要也从三个方面来做:
- 广告页
- 数据预先加载
- View的预创建
广告页优化
闲鱼之前的广告页的流程如下图:
先拉起启动页,然后启动页拉起首页,首页再拉起广告页,广告页起来先展示默认图,然后同时去做是否有广告的判断,然后再去做广告的展示,这个过程如果没有广告,也会让默认的广告页展示3秒钟再关闭。
这个过程显然是不合理的,广告有自己的疲劳期,那么在没有广告的时候,拉起广告页就是一个浪费。其次广告页作为一个Activity拉起,需要经历一些IPC的调用,整个操作也是比较重的。
基于这两点,我们在广告页这块,先在初始化的时候就做提前的资源拉取和预判,这样如果确实没有广告资源,那么广告页直接不做启动,节省启动资源。其次,我们将广告页由一个activity,改造成一个全屏展示的Dialog,进一步来节省广告拉起时资源消耗,让首页其他内容的加载有更充足的系统资源。
数据预先加载
在性能优化中,空间换时间与提前预加载就像广为人知的”中间加一层“一样好用。
闲鱼首页必须的两个接口,冷启和热启接口耗时在1秒左右,而他们是在首页第一帧回调回来之后的时机才开始请求的。这里完全可以把请求的时机提前到初始化的过程中并行去做,从而为闲鱼启动-1s。
于是我们设计了针对这种情况下的预取模块,在初始化的时候,就去做首页数据的预加载,整体的模块的时序如下:
这一步做完之后,本地机器测试结果大约节约了950ms的启动时间。
View预创建
在解决完数据的问题之后,我们通过魔镜平台,会发现在y67上,首页展示之后,有大量的白屏的时间,view的创建和渲染,在这里消耗了大量资源,并占用了很长的时间(这里每一帧是100ms),平均大概在1400ms
于是我们自然而然的想到了在初始化的过程中去提前创建view,但是如果是在初始化过程中的主线程去创建view,那么势必会跟启动页和广告页等ui元素竞争主线程的使用,基本等于白干。
于是这里我们采用在子线程预先创建view并执行mesure与layout操作。等待首页渲染时,使用对应的id进行取出和使用。做完之后,会发现view的上屏时间,在y67上缩短到600ms,减少了一倍的的时间:
总结与下一步优化
通过上面的方式,整个启动阶段的时间从2.5s降低到了1.3s,降低了将近一倍的时间。另外启动任务所消耗的总时间从8s降低到了3s。首页的渲染几乎达到了秒出。整体启动时间降低到了4.5s左右。
这个阶段主要是对启动过程中的任务与首页代码本身的优化。下一阶段,我们会对整个启动过程中的运行环境进行优化:
- 对启动时候的资源消耗进行整理,减少不必要的网络请求与IO以及线程切换。
- 对启动器中的线程负载进行优化,目前启动的任务分配方式距离理论上的最优值(平均值)大约还有50%的空间。
- 使用dex-relayout、PGO加速启动

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
我们为什么要做 SoloPi
SoloPi现状 去年(2019年)7月份,蚂蚁集团正式对外开源了客户端自动化测试工具 SoloPi ,其主要包括三大模块:录制回放(用于功能测试)、性能工具(用于性能测试)以及一机多控(服务于兼容性测试)。从开源至今,我们也陆续收到了公司内外对工具的不同声音,有的同学对这套工具能提升测试同学的效率表达了支持态度,也有同学认为脱离了代码的自动化测试可能会限制测试同学的灵活度,让这种模式很难走远,还有同学认为这套工具只是昙花一现,简单包装了一下PC上的工具,而缺乏实际的创新。 其实,这套工具我们从17年开始研发,至今已经有三年的历史了,最开始SoloPi本身只是一个性能测试工具,随后逐步扩展成涵盖功能测试、性能测试、兼容性测试、异常测试、Mock测试等一系列移动端测试场景的测试框架,这一路走来,SoloPi就像我们的孩子一样一步一步地成长,稳步且有计划的前进着。 回想当初,SoloPi最开始想解决的诉求很简单:测试很麻烦,我们想要更简便的测试方法。所以,我们从当时最麻烦的性能测试入手,以往的性能测试工具,无外乎三种形态:PC驱动工具、侵入式的测试模块、ROOT工具。 PC工具:除了And...
- 下一篇
直播回放:快速上手,使用 Kotlin 把支付宝小程序装进自己的 App
写一个 Android App 或许不难,但企业对于移动应用的要求愈来愈高,不只要求开发速度、稳定度、质量等,甚至希望能具备动态扩展的架构设计、在 App 中自启动小程序。面向这些需求,若是有好的开发工具及平台的支持,将可以大大降低开发及运维的成本。本次网络研讨会特别邀请到支付宝高级无线开发工程师温盛章为大家演示用 Kotlin 开发移动应用,并集成 mPaaS 让 App 具备小程序能力。 ▶点击观看全程回放 主题分享 本次分享共有三个关键字:Kotlin 、 小程序 、 mPaaS 。温盛章首先从用户、开发、技术、平台等四大视角,向大家说明小程序是什么?简单来说,小程序就是一种拥有完整生命周期、应用间相互隔离、独立运行于宿主应用内的应用。而小程序从工作型 App、平台型 App、超级 App 到新阶段一路的演化历程,目前已经可以有接近 Native 的体验和顺畅、也可以有 H5 的快捷发布的优势。 对小程序有概念后,温盛章就以 Android Studio 演示如何在一个 Mobile App 里,以 Kotlin 撰写 Mobile App 的代码,并接上 mPaaS 平台,让 ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- MySQL8.0.19开启GTID主从同步CentOS8
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7设置SWAP分区,小内存服务器的救世主