爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

栏目: Android · 发布时间: 7年前

内容简介:本文由爱奇艺技术团队原创分享,原题《爱奇艺Android客户端启动优化与分析》。互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。爱奇艺安卓APP非常重视启动速度的优化,本文将从启动过程、启动时间测量、启动优化、以及后续监控等方面分享我们在启动优化方面积累的经验。

本文由爱奇艺技术团队原创分享,原题《爱奇艺Android客户端启动优化与分析》。

1、引言

互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Android APP而言,要求更加严格,如果系统无响应时间超过5秒,便会出现ANR,APP可能会被强制关闭,因此,启动时间作为一个重要的性能指标,关系着用户的第一体验。

爱奇艺安卓APP非常重视启动速度的优化,本文将从启动过程、启动时间测量、启动优化、以及后续监控等方面分享我们在启动优化方面积累的经验。

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

相关文章:

移动端IM实践:Android版微信如何大幅提升交互性能(一)

移动端IM实践:Android版微信如何大幅提升交互性能(二)

移动端IM实践:iOS版微信界面卡顿监测方案

微信团队原创分享:Android内存泄漏监控和优化技巧总结

美图App的移动端DNS优化实践:HTTPS请求耗时减小近半

(本文同步发布于: http://www.52im.net/thread-2221-1-1.html

2、启动模式

要准确的测量APP的启动时间,首先我们要了解APP整个启动过程。

启动过程,一般可以分为以下三类:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

从上图可以看出,启动过程中,Cold的模式下,生命周期中做的事情最多,启动的时间最长。因此,我们以冷启动来衡量APP启动时间。

那么启动过程中,如何判断哪些生命周期影响启动速度呢?请继续往下读。

3、启动过程

我们知道,APP的启动和运行,就是 Linux 系统创建进程和组件对象,并在UI线程中处理组件消息的过程。

启动过程图:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

App的启动过程,可以划分为三个阶段,下面就各个阶段进行详细讲解。

3.1 创建进程

当APP启动时,如果当前app的进程不存在,便会创建新的进程;App主进程启动后,如果启动某个组件,并且该组件设置了android:process属性,组件所运行的进程不存在,也会创建新的进程。

需要注意的是,如果在启动阶段,初始化的组件中,包含了多个进程,便会创建多次进程,BindApplication操作也会重复执行多次

3.2 创建UI线程及Handler

进程创建后,会通过反射,执行ActivityThread入口函数,创建Handler,并在当前线程中prepareMainLooper,并在Handler中接收组件的消息。

我们来看一下Handler中处理的消息:

1)LAUNCH_ACTIVITY:启动,执行Activity;

2)RESUME_ACTIVITY:恢复Activity;

3)BIND_APPLICATION,启动app;

4)BIND_SERVICE:Service创建, onBind;

5)LOW_MEMORY:内存不足,回收后台程序。

sMainThreadHandler中,处理的消息很多,这里只罗列了,可能在启动阶段可能会执行的操作, 这些操作都是运行在Main Thread中,对启动而言,属于阻塞性的。

Activity生命周期,自然需要在启动阶段执行,但,对于Service的创建,Trim_memory回调,广播接收等操作,就需要重点考虑,其操作耗时性。

3.3 Activity运行及绘制

前两个过程,创建进程和UI线程及Handler,都是由系统决定的,对APP开发者而言,并不能控制其执行时间,在本阶段,执行BindApplication,和Acitivity生命周期,都是可以由开发者自定义。

Activity执行到onResume之后,会执行至ViewRootImpl,执行两次performTraversals,第二次traversal操作中,会执行performDraw操作,同时通知RenderThread线程执行绘制。

从启动的三个阶段,我们可以看出,启动启动时间的长短的决定因素在于:主线程中所做事情消耗的时间的多少。

所以: 我们的优化工作主要集中在,排查主线程中耗时性的工作,并进行合理的优化。

Android手机,系统的资源是有限的,过多的异步线程,会抢占CPU,导致主线程执行时间片间隔增大。同样的,内存消耗状态,GC频率,也会影响启动的时间。

4、分析及测量

通过上述的源码的解读,我们已经了解了启动过程,以及可能引起启动过慢的原因。接下来介绍一些常用的分析手段及时间测量方法。

我们的启动分析 工具 主要使用SysTrace,具体的使用方法请参考官网文档: https://developer.android.com/studio/command-line/systrace

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

▲ Andriod上的各种各样分析工具,请自行选用

4.1 SysTrace分析技巧

【4.1.1、UI Thread 颜色显示】

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

绿色:Running

白色:Sleeping

棕色:Uninterruptible Sleep

橙色:Uninterruptible Sleep - Block I/O

其中10ms以内的,较短时间的Sleeping状态,不用关注,可能是由于CPU调度的时间片分配间隔引起的;较长时间的Block I/O和Sleep状态,可以确定有阻塞启动的逻辑在这个阶段运行,需要进一步对代码进行分析定位。

【4.1.2、查看CPU状态及线程运行时长】

查看CPU占用状态:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

线程执行:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

通过该阶段密集程度,反映出CPU占用率,也能在一定程度上反映出该阶段执行时间被阻塞情况;线程执行情况统计,可以查看线程执行时间排名,对执行时间较长的子线程进行优化。

4.2 SysTrace启动时间

在SysTrace图中,UI Thread中包含了bindApplication,activityStart,traversal等操作,RenderThread中包含DrawFrame等操作。这些TAG节点是源码已经添加的,可参考#3.2中介绍。

Trace上启动时间:

从bindApplication至第二次traversal完成,可认为UI第一次绘制完成,启动完成。选中开始点和结束点,可以查看过程消耗的时间。

4.3 adb shell am start -W

在统计APP启动时间时,系统为我们提供了adb命令,可以输出启动时间

TotalTime: 

表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用 Activity pause 的耗时。

系统在绘制完成后,ActivityManagerService会回调该方法,统计时间不如SysTrace准确,但是能够方便我们通过脚本多次启动测量TotalTime,对比版本间启动时间差异。

4.4 埋点

通过APP启动生命周期中,关键位置加入时间点记录,达到测量目的。

4.5 录屏

录屏方式收集到的时间,更接近于用户的真实体感。

5、优化总结

为了让用户在进入APP之后,更快更流畅的使用服务,所以会在启动过程中,提前对一些基础库和组建进行初始化操作,这就意味着系统有限的资源会被抢占,影响启动时间。启动时间的优化,是一个平衡性能和体验的过程。

通过Systrace工具分析,我们发现爱奇艺爱奇艺安卓APP启动过程中一些问题,接下来,我们就结合具体的业务实践,进行启动问题进行优化。

5.1 区分进程初始化Application

由第3章我们了解到,对于一个app而言,App内组件可以运行在不同的进程之中。

举个例子:

一个APP拥有主进程,插件进程,下载进程三个进程,会在启动阶段创建相应的组件,但只有一个QYApplication继承自系统Application,创建三次进程,QYApplication中attach(),onCreate()方法都会被执行三次。

每个进程说需要初始化的内容肯定是不一样的,所以,为了防止资源的浪费,我们需要区分进程,初始化Appcation.

成果: 对多进程应用而言,通过对初始化内容进行梳理,合理区分初始化,会大幅减少内存和CPU占用。

5.2 异步处理耗时任务

子线程处理耗时任务,主线程做的事情越少,越早进入Acitivity绘制阶段,界面越早展现。

注意:

1)不在主线程做耗时任务,如文件,网络等;

2)启动阶段初始化任务,尽量在异步线程处理;

3)主线程,不用等待或者依赖于子线程任务。

进一步优化: 可以自建线程池,维持一定线程个数,管理任务队列。

5.3 防止多线程抢占CPU

Android系统资源有限,特别是CPU资源,理论上来说,UI线程执行的任务,也无法保证一直被调度状态,当并发的线程数过多,UI线程时间片会更短,从而导致启动时间被变慢。

下面罗列一些常见,容易造成CPU被抢占的场景:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

成果: 通过对执行时间较久,执行频率的业务进行优化,将CPU占有率维持在合理的程度,会大幅减少启动时间,减少300ms以上。

5.4 系统API使用

部分系统的API使用是阻塞性的,文件很小可能无法感知,当文件过大,或者使用频繁时,可能造成阻塞。

例如:

1)SharedPreference.Editor提交操作:
  - commit方法属于属于阻塞性质API,建议使用apply;
  - 此外,我们知道,SP文件的存储是一个XML文件,以key-value形式存储,当业务过多时,需要拆分为多个文件存储,防止文件过大,出现读取耗时及ANR;
  - 进一步优化,可对启动阶段,频繁的SP操作在内存中,统一提交。
2)AssetManager.open操作: 
Android开发中,我们有时会将资源文件放在assets目录中,然后使用open操作读取文件,如果文件过大,需要在异步线程中执行。

成果: 随着业务量日积月累,正常的系统API的使用,也可能出现问题,通过排除,可减少50-100ms。

5.5 精简布局

布局的复杂程度,直接影响绘制的时间。

举个例子:

在启动过程中,会有需要大的背景图,只有第一次安装时使用,后续属性设置为android:visibility="gone",但是,虽然设置了gone属性,不会显示,但依旧会被解析。

建议:

1)减少布局层次;

2)无用资源使用ViewStub,使用时加载。

成果: 启动阶段的布局较简单,通过优化背景图片的加载,减少50-100ms。

5.6 Service延后初始化

App启动中过程中,经常进行Service初始化操作,由于Service使用一般不涉及界面,可能会认为初始化生命周期不在主线程中,其实不然,在3.2的启动过程源码介绍中讲到,Service的生命周期,也属于主线程Handler接收的Message之一。

建议: Service生命周期中,注意逻辑执行时间性能优化,初始化尽量延后。

成果: 取决于初始化Service的生命周期执行时间,可减少200ms以上。

5.7 将任务delay至首页绘制完成后

对于APP首页展示不需要的初始化逻辑,可延后至首页绘制完成后初始化。

注意: 需要post两次才能保证在第一次绘制之后显示,因为,系统绘制会执行两次Performtraversal。

进一步优化: 可将业务逻辑的初始化划分为,首页绘制后,5s,10s,20s三个阶段分别初始化,防止首页绘制执行任务过多造成掉帧。

成果: 释放绘制阶段的CPU,可将复杂的绘制提前200ms以上。

6、性能监控

稳定的用户体验依赖于持续的监控,爱奇艺为监控启动性能建立了一套监控体系、测试、工具、开发等几个团队从不同的纬度搭建不同的监控方案。

监控方案如下:

1)测试: 录屏,从用户的真实体验角度,获取最准确的启动时间;

2)实时监控: 通过埋点,大数据采样投递获取真实线上环境数据,从地域,时间,机型,app版本,系统版本等各个纬度对启动时间进行监控;

3)脚本测试: 通过对脚本,对同一收集多次启动数据进行收集,通过不同版本间的对比,监控启动时间的变化情况。

7、SysTrace扩展

SysTrace通过TAG节点可以清晰展现,启动过程以及方法执行时间,但是,从发现问题,然后通过节点去定位问题,是一件很繁琐的工作,如果你们工程编译又比较慢,简直让人崩溃。

比如: 自动化TAG注入。

在Android工程编译的过程中,指定class,在方法前后,自动化插入Trace节点,统计方法执行时间。

流程:

1)在编译的过程中,插入自定义Task任务;

2)读取配置文件,文件中包含了需要注入 java 文件名和路径名和method;

3)找到需要注入的class文件,然后通过ASM改变字节码,方法前后,插入自定义自定义方法。

通过工具的操作,能够做到不用修改原有工程文件,自动在打包时注入TAG节点和逻辑代码,配置文件可以循环利用,提高分析效率,节能环保。

8、优化成果

启动时间,由于不同的机型性能同,Android系统版本不同,同一APP版本启动时间,相差很大,所以统计一般以同一手机,不同版本做比较,尽量保证手机状态一致。

SysTrace手机优化时间对比:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

脚本多次启动时间收集对比:

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

经过多个版本的持续优化,有无广告两种不同的场景下,启动时间分别减少40%和35%,启动速度得到了较大的提升。

9、本文小结

启动时间的优化和监控,是一项长期的任务,需要对异常的情况进行分析,对可能造成阻塞的代码逻辑进行合理的优化,非常感谢各个业务团队支持和配合。

以上就是全部启动时间优化相关的内容,谢谢大家能够阅读到这里,如果有更好的建议,欢迎交流!

附录:更多移动端高级开发技术分享

全面了解移动端DNS域名劫持等杂症:技术原理、问题根源、解决方案等

金蝶随手记团队分享:还在用JSON? Protobuf让数据传输更省更快(原理篇)

金蝶随手记团队分享:还在用JSON? Protobuf让数据传输更省更快(实战篇)

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

通俗易懂:基于集群的移动端IM接入层负载均衡方案分享

QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率

腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)

腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(下篇)

基于社交网络的Yelp是如何实现海量用户图片的无损压缩的?

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

最火移动端跨平台方案盘点:React Native、weex、Flutter

iPhone X 的 UI界面适配官方指南!》

新浪微博技术分享:微博短视频服务的优化实践之路

全面掌握移动端主流图片格式的特点、性能、调优等

迈向高阶:优秀Android程序员必知必会的网络基础

HTTPS时代已来,打算更新你的HTTP服务了吗?

移动端APP的日志上报机制的优化实践

移动端网络优化之HTTP请求的DNS优化

伪即时通讯:分享滴滴出行iOS客户端的演进过程

Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]

微信团队原创分享:Android版微信从300KB到30MB的技术演进

Android程序员的痛你永远不懂(一):Bitmap到底占用多大内存?

Android程序员的痛你永远不懂(二):如何减少Bitmap内存占用?

Android反编译利器APKDB:没有美工的日子里继续坚强的撸

全面总结iOS版微信升级iOS9遇到的各种“坑”

微信团队原创资源混淆工具:让你的APK立减1M

微信团队原创Android资源混淆工具:AndResGuard [有源码]

Android版微信安装包“减肥”实战记录

iOS版微信安装包“减肥”实战记录

iOS端移动网络调优的8条建议

微信“红包照片”背后的技术难题

移动端IM实践:iOS版微信小视频功能技术方案实录

移动端IM实践:iOS版微信的多设备字体适配方案探讨

爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

>> 更多同类文章 ……

(本文同步发布于: http://www.52im.net/thread-2221-1-1.html


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning XSLT 2.0

Beginning XSLT 2.0

Jeni Tennison / Apress / 2005-07-22 / USD 49.99

This is an updated revision of Tennison's "Beginning XSLT", updated for the new revision of the XSLT standard. XSLT is a technology used to transform an XML document with one structure into another ......一起来看看 《Beginning XSLT 2.0》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具