前言
本文是本人学习Android app 启动优化的总结,由于本人知识有限,难免有错误或疏漏的地方,请大家多多指正。
为什么要学习APP的启动优化?
用户希望应用能够及时响应并快速加载。启动时间过长的应用可能会导致用户在对应用给出很低的评分,甚至完全弃用。
APP 启动流程简述
首先我们先简单描述一下App的启动流程(以冷启动为例,什么是冷启动下面会有讲解)
(1)用户点击桌面上的app Icon,Launcer进程采用Binder IPC方式向system_server发送startActivity请求
(2)system_server进程接收到请求后,向zygote进程发送创建App进程的请求
(3)Zygote进程fork出新的子进程,即App进程
(4)App进程通过 Binder IPC方式向system_server 发送attachApplication请求
(5)system_server接收到相关请求后,做出一系列的准备工作,通过Binder IPC向App进程发送scheduleLaunchActivity的请求
(6)App进程的binder线程(即ActivityThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息
(7)主线程在收到message后,通过反射的方式创建相关的Activity,并回调Activity的onCreate等方法
(8)至此,App便正式启动,开始进入Activity的生命周期,执行完onCreate/onStart/onResume,渲染完UI界面后便可以看到App的主界面。
App的启动类型
冷启动:冷启动是指App从头开始启动,系统进程在冷启动时才会创建App进程。发生冷启动的情况发生自系统启动后第一次开启应用和应用彻底结束后的再一次打开。
热启动:在热启动时,所有的工作就是将App带到前台。只要应用的所有Activity仍滞留在内存中,应用就不会重复执行对象的初始化、布局加载和绘制。
温启动:温启动包含了冷启动的部分操作,同时,他的开销比热启动要高。与许多状态可以被视为温启动。
(1)用户在退出应用后马上再次重启应用,此时App的进程还没有被销毁,继续运行,但应用需要重新开始从onCreate重新创建Activity。
(2)系统将应用从内存中释放,然后用户又重新启动它。进程和 Activity 需要重启,但传递到 onCreate() 的已保存的实例savedInstanceState对于完成此任务有一定助益。
启动耗时统计
在性能测试中遵循2-5-8原则:
当App启动的响应时间小于2s,客户认为app启动速度很快
当App启动的响应时间介于2-5秒之间,客户认为app启动速度还可以
当App启动的响应时间介于5-8秒客户认为app启动速度很慢,但还可以接受
当App启动的响应时间大于8秒,客户认为app启动速度是不可接受的,甚至认为App没有响应。
测试App的启动耗时统计主要有如下的两种方法
(1)系统日志统计
在Android 19版本以上,每次启动时都会打印开启app启动时间的名为Displayed的值。该值代表此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间。
(2)命令方式统计
adb shell am start -S -W [packageName]/[activityName]
WaitTime:包括前一个应用Activity pause的时间和新应用启动的时间;(这一个在我的设备上没有相关的数据打印)
ThisTime:表示一连串启动Activity的最后一个Activity的启动耗时;
TotalTime:表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时。
如何进行启动时间的性能调优
注意:其实这个话题对我这种初学性能调优的人来说肯定是说不了很全面,甚至可能有一些是错误的,下面的内容我将结合我所知道的相关知识进行讲述
如果对启动时间不满的话,就需要分析启动时间上的瓶颈。这时就需要借助Android Studio的CPU Profile了。使用方式如下
(1)点击app,选择Edit Configurations
(2)选择右侧Profiling选项卡,勾选Start recording CPU activity on startup 旁边的复选框,选择CPU的记录配置,共有四种配置,分别是
Sample Java Methods
对 Java 方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈。分析器会比较捕获的数据集,以推导与应用的 Java 代码执行有关的时间和资源使用信息。如果应用在捕获调用堆栈后进入一个方法并在下次捕获前退出该方法,分析器将不会记录该方法调用。如果您想要跟踪生命周期如此短的方法,应使用检测跟踪。
Trace Java Methods
跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。
Sample C/C++ Functions
对 C/C++ 函数采样:捕获应用的原生线程的采样跟踪数据。要使用此配置,您必须将应用部署到搭载Android 8.0(API 级别 26)或更高版本的设备上。
Trace System Calls
跟踪系统调用:捕获非常翔实的细节,以便您检查应用与系统资源的交互情况。您可以检查线程状态的确切时间和持续时间、直观地查看所有内核的 CPU 瓶颈在何处,并添加要分析的自定义跟踪事件。要使用此配置,您必须将应用部署到搭载 Android 7.0(API 级别 24)或更高版本的设备上。此跟踪配置在 systrace 的基础上构建而成。您可以使用 systrace 命令行实用程序指定除 CPU Profiler 提供的选项之外的其他选项。systrace 提供的其他系统级数据可帮助您检查原生系统进程并排查丢帧或帧延迟问题。
在这里选择Trace Java Methods。点击apply
(3)在运行时直接点击Profile即可
(4)在主页能够完全显示且具备交互能力(或者你自己觉得合适的时机)后点击stop,停止CPU的监控,这时会解析相关的Trace Record
(5)此时展示分析后的结果
那么这个如何进行分析呢?首先看一下相关的几个分析方法
Flame Chart
提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或函数收集起来,并在火焰图中将它们表示为一个较长的横条 。横轴显示的是百分比数值。由于忽略了时间线信息,Flame Chart 可以展示每次调用消耗时间占用整个记录时长的百分比。 同时纵轴也被对调了,在顶部展示的是被调用者,底部展示的是调用者。此时的图表看起来越往上越窄,就好像火焰一样,因此得名: 火焰图。
Top Down Tree
如果我们需要更精确的时间信息,就需要使用 Top Down Tree。 Top Down Tree显示一个调用列表,在该列表中展开方法或函数节点会显示它调用了的方法节点
Bottom Up Tree
方便地找到某个方法的调用栈。在该列表中展开方法或函数节点会显示哪个方法调用了自己。就是Top Dwon Tree的倒置
一般我们在这三种图中,只要看Top Down Tree就好
那么如何进行启动时间的优化呢?我认为可以采取如下几步
(1)首先找耗时最多的方法,点进看这个方法调用中那个方法调用耗时长
(2)如果耗时长的方法层级很深且都是在调用系统的方法那么这个方法可能是我们无法进行优化的,在去寻找第二长的方法链进行调用
(3)一般可以在主界面调用onCreate进行分析
简单例子分析
现在我们以一个简单的例子来分析一下如何进行app的启动优化。
(1)首先我们看一下app的启动时间
根据启动时间我们可以看到冷启动该app耗费了3.4s
(2)打印CPU的Profile,可以看到
在onCreate中调用了sleep方法,该方法耗时为3.2s
(3)查看源码发现在MainActivity的onCreate方法中调用了SystemClock.sleep方法进行了休眠。
(4)去除休眠后我们在打印启动时间,发现启动速度有了很大的提升(这是因为我们故意睡眠了3s,实际上可能提升的速度很轻微,甚至看不出来。)
至此Android app启动时间优化介绍完成。相关的实战将在介绍完布局优化后以单独的文章进行介绍。