切换菜单
搜索
个人笔记云
首页
java
spring
springmvc
python
使用教程
笔记管理
搜索
登录/注册
好物分享
退出
搜索
Android性能优化-App启动优化
2021-10-23
741
**参考视频教程:** [**Top团队大牛带你玩转Android性能分析与优化 **](http://www.notescloud.top/goods/detail/1409) 官方文档地址:[https://developer.android.google.cn/topic/performance/launch-time.html#common](https://developer.android.google.cb/topic/performance/launch-time.html#common) 本文转自:[](http://www.lightskystreet.com/categories/Android/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/)[lightSky'Blog](http://www.lightskystreet.com/) 通常用户期望app响应和加载速度越快越好。一个启动速度慢的app很可能会给用户留下不好的印象,除了导致用户在应用市场上的打分低之外,很有可能导致致用户直接卸载。 这篇文章提供了优化app启动时间的方法。先解释了app进程启动的内部流程。然后讨论如何优化启动的性能。最后列出几个常见的启动问题和解决方案。 **一 启动内幕** ---------- App启动可能发生在以下三种状态 之一,每一种都会影响到展现给用户的时间:冷启动、热启动和温启动(翻译的有点怪,介于冷和热之间吧)。 冷启动下,app所做的事情不较多,其它两种情况,系统只需要将app从后台切到前台。建议你在冷启动的基础上做优化,这样也会提升热启动和温启动的性能。 为了更好地优化app的启动,了解系统和app层做了什么以及如何相互影响很有必要。 ### 1.1 冷启动 冷启动指:在app启动之前,系统的进程还没有,直到app启动创建app的进程。冷启动会发生在device重启或者app被杀死的情况下。这种启动在优化启动时间上,有更大的挑战,因为相比其它两种启动方式,系统和app有更多的工作需要处理。 冷启动之前,系统会执行以下三个task: 1、加载并启动app 2、在app启动后,立即展示空白的window 3、创建app进程 一旦系统创建了app进程,那么app进程就会执行以下步骤 1、创建app对象 2、启动main thread 3、创建MainActivity 4、Inflate view 5、布置屏幕 6、进行首次绘制 一旦app进程完成了第一次绘制,系统进程就会用main activity替换已经展示的background window。之后用户才可以使用app。 下图展示了系统和app进程互相如何工作的,展示了app启动时期的几个重要部分,在创建app和main activity之间,我们可以提升性能问题 [](http://www.lightskystreet.com/img/android-optimize-app-start/app-launch-flow.png)  [image](http://www.lightskystreet.com/img/android-optimize-app-start/app-launch-flow.png)
#### Application的创建 当应用启动的时候,空白的window在app第一次完成绘制之前都会存在。在那之后,系统进程才会替换启动窗口,允许用户开始和app交互。 如果你复写了 `Application.oncreate()` 方法,app启动的时候,会调用该方法。之后,app会孵化主线程(UI线程),并通过它来创建main activity。 从这之后,系统和app级别的进程将会按照[app lifecycle stages](https://developer.android.google.cn/topic/performance/%7BdocRoot%7Dguide/topics/processes/process-lifecycle.html) 执行。 #### Activity的创建 在app进程创建了Activity之后,Activity将会执行以下操作 1、初始化值 2、调用构造函数 3、调用毁掉方法,比如Activity.onCreate()。 通常,onCreate方法会对加载时间有比较大的影响。因为它将执行繁重的工作:加载和填充view,并初始化Activity运行期间需要用的对象。 ### 1.2 热启动 相对于冷启动,热启动会简单的多。如果app的所有Activities还存在内存中,那么系统需要做的就是将activity切换到前台。这样app会避免进行的对象初始化,布局填充和渲染。 但是,如果一些内存在触发内存回调方法的时候被回收了,比如onTrimMemory(),那么这些对象就需要重新创建。 热启动会和冷启动有相同的行为。系统也会展示一个空白的window,知道app完成Activity的渲染。 ### 1.3 温启动 温启动做的工作介于冷热启动之间。这里列举几种可能被认为是温启动的状态: 1、用户离开了app,然后重新启动它。这时进程还在继续运行,但是Activity被回收了,app需要重新创建activity。 2、系统将你的app回收了,然后用户重新启动app。进程和Activity都需要重新启动,但它们可以从onCreate方法保存的bundle中恢复。 二 **优化启动性能** ------------ 为了确定启动时间的性能问题,我们需要先确定app启动花费了多少时间。 ### 2.1 Time to initial display 从4.4(API 19)开始,logcat会输出带有Displayed的log。该值代表从app启动进程到完成Activity第一次绘制的时间。该时间内完成了一下流程: * 启动进程 * 初始化对象 * 创建和初始化Activity * 填充布局 * 第一次绘制app 打出的log如下: **ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms** 如果你从命令行或者终端跟踪log的话,可以比较直接的定位到该log。如果在AndroidStudio中,别忘记关闭filter。 [](http://www.lightskystreet.com/img/android-optimize-app-start/time-display.png)  [image](http://www.lightskystreet.com/img/android-optimize-app-start/time-display.png)
Displayed值并没有捕获所有资源都被加载和展示的总时间。那些不在layout文件中或者创建app初始化所需要对象的时间不包含在内。因为这些资源是在一个内部进程中加载的,并且不会阻塞app的初始化展示。 ### 2.2 Time to full display 你可以调用[reportFullyDrawn()](https://developer.android.google.cn/reference/android/app/Activity.html#reportFullyDrawn())方法去测量从应用启动到所有资源和view层级都被绘制出来的时间。这对于app执行懒加载的情况很有用。在懒加载中,app不会阻塞window的初始化绘制,但同步进行资源加载和view的更新会阻塞。 由于懒加载,app的初始化展示不会包含所有的资源。你可以考虑完全加载并展示所有资源和view的时候作为一个考量。比如,UI可能完全加载了,包括一些text的绘制,但是由于图片需要从网络获取,这时还没有展示。 为了处理这种情况,你可以手动地调用reportFullyDrawn方法让系统知道你的activity已经通过懒加载完成了。但你是用该方法的时候,logcat展示的时间就包含从应用被创建到reportFullyDrawn方法被调用的时间。 #### **定位瓶颈** 两种方式可以帮助你定位问题:AndroidStudio中的Method Tracer和内嵌tracing代码的方式。更多可以参考[documentation](https://developer.android.google.cn/topic/performance/%7BdocRoot%7Dstudio/profile/am-methodtrace.html). 如果无法使用Method Tracer Tool ,或者觉得trace的时机不够准确,那么你可以通过在app和Activity的onCreate方法中嵌入代码进行追踪,比如写下追踪代码。更多信息,可以参考[Trace](https://developer.android.google.cn/reference/android/os/Trace.html) 、[Systrace](https://developer.android.google.cn/topic/performance/%7BdocRoot%7Dstudio/profile/systrace-commandline.html) 三 **常见的问题** ----------- ### 3.1 繁重的App初始化 当你继承了Application对象,又在Application对象进行初始化的时候执行繁重的工作或者复杂的逻辑,那么就可能导致启动的性能问题。在启动的时候花一些时间去初始化一些子类可能完全没必要。 在app初始化的时候,其它的挑战包括垃圾回收事件,繁重的操作,比如I/O,都有可能会阻塞进程的初始化。对于Dalvik运行环境来说,垃圾回收是一个需要特别考虑的点,Art运行环境会并发的执行垃圾回收,以便最小化垃圾回收产生的影响。 #### 3.1.1 **定位为题** 使用method tracing或者内嵌代码来定位这个问题 ##### Method tracing Running the Method Tracer tool reveals that the `callApplicationOnCreate()` method eventually calls your`com.example.customApplication.onCreate` method. If the tool shows that these methods are taking a long time to finish executing, you should explore further to see what work is occurring there. ##### **内嵌代码的方式** 可以对以下代码进行追踪 1、App的onCreate方法 2、onCreate中初始化的所有全局单例对象 3、所有I/O,反序列化,或者可能导致性能问题的循环 #### **3.1.2 解决方案** 如果是由于不必要的初始化或者硬盘I/O操作导致的问题,解决方案就是懒初始化对象:只初始化立即需要的。而不是在一开始就创建全局的静态对象,可以将它们的初始化放在一个单例中,当app首次访问它们的时候再初始化对象。 3.2 **繁重的Activity初始化** ---------------------- Activity的创建有时会承担大量的复杂操作。通常这里存在可以优化的点。常见的问题有: 1、填充大量复杂的布局 2、硬盘操作或者网络操作阻塞了绘制 3、加载或者编码bitmap 4、栅栏化VectorDrawable对象 5、Activity中其它子系统的初始化 ### 3.2.1 **定位问题** 和定位App启动问题类似,也是通过method tracing或者嵌入代码来定位。 ##### Method tracing 当执行Method Tracer tool的时候,你应该关注继承于Application的子类的构造函数和onCreate方法。 如果该工具表明代码中花了很长时间去执行,那么你就应该进一步查看这里的具体操作。 **嵌入代码的方式** 追踪的部分可能是以下代码块(和App初始化一样) 1、App的onCreate方法 2、启动时初始化的所有全局单例对象 3、所有I/O,反序列化,或者可能导致性能问题的循环 ### **3.2.2 解决方案** 上面可能有很多潜在的问题,这列举两种通用的问题和解决方案: * view的层级越庞大,app就会花越多的时间去填充它 减少多余的或者嵌套的布局 不填充哪些不需要在启动时就需要展示的view。可通过ViewStub来实现,在需要的时候再填充 * 在main thread中做资源的初始化也会减慢启动速度。可以通过下面来解决 延迟所有的资源初始化或者放在其它线程中去做 允许app先加载和展示view,那些依赖于bitmap或者其它资源之后再去更新 **三 主题化的启动屏幕** -------------- 我们可以通过主题化app的启动屏幕来改善启动体验。这样整个app的启动和接下来的操作会显得更加连贯。但这样只是将Activity的慢启动问题隐藏了。 一种常用的方式实现主题启动屏幕的方式是使用 `windowDisablePreview`主题属性关闭系统进程在app启动时绘制的初始化空白屏幕。但是,这种方式会导致更长时间。同样的,这样也会迫使用户等到Activity启动后才会得到反馈,会让用户产生app本身是否有问题的困惑。 ### 解决方案 建议你不应该禁止预览窗口,你应该遵循 [Material Design](http://www.google.com/design/spec/patterns/launch-screens.html#) 标准。使用Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawable。 **布局文件**  layout.png
**Manifest file:**  Manifest.png
然后在代码中将主题切换回app的主题,最简单的方式是在`super.onCreate()` 和`setContentView()方法之前`调用 `setTheme(R.style.AppTheme)` ``` public class MyMainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Make sure this is before calling super.onCreate setTheme(R.style.Theme_MyApp); super.onCreate(savedInstanceState); // ... } } ``` 以上是官方文档的相关讲解,以下是自己关于APP启动优化一点见解: (1)APP启动优化,首先我们应该知道APP的启动过程,APP启动相关内容可以参考以下文章; [从点击App图标到View显示出来主要流程(一)源码解析Activity启动流程](https://www.jianshu.com/p/04997a321d43) [从点击App图标到View显示出来主要流程(二)源码解析setContentView主要工作及View整体布局层次](https://www.jianshu.com/p/03dea45e0b4f) [从点击App图标到View显示出来主要流程(三)源码解析View测量布局绘制流程](https://www.jianshu.com/p/4c5a1541f641) (2)其次,我们应该知道如何统计APP启动时间,知道如何统计启动时间之后,我们才能知道我们应用的启动速度如何;**关键点是知道那些操作导致比较耗时** ,最简单暴力的办法是在代码添加时间打印,打印应用启动每一阶段的时间,找到比较的操作。 (3)最后,需要知道哪些因素会对启动速度产生影响,知道应用启动优化通常的做法有哪些。
教程分类
热门视频教程
热门文章
热门书籍推荐