Android优化App启动时间

原文地址:https://developer.android.com/topic/performance/vitals/launch-time

用户希望App能够快速相应和加载,应用启动缓慢会带来糟糕的用户体验,导致用户恶评,甚至会卸载你的应用。

这篇文章提供的信息能够帮助你优化应用的启动时间。首先,我们先来了解应用启动的内部原理,接下来,我们会讨论如何分析启动性能。最后,最后我们会介绍一些影响启动性能的常见问题,并会给出相应的解决办法。

应用启动原理

应用启动可以分为三种类型,冷启动,暖启动,热启动,每种类型所花费的时间是不一样的。
冷启动模式下,应用进程完全不存在,系统要新建应用进程。在另外两种模式下,系统只需要将运行中的应用从后台切换到前台。建议你针对冷启动模式做优化。冷启动速度得到提升了,暖启动和热启动也会变得很快。

那么在应用启动过程中,Android系统和应用层都做了那些操作呢?理解了它们的内部原理,将会帮助我们做好启动性能优化。

冷启动

冷启动指应用重新开始创建:在启动之前,系统进程尚未创建出应用进程。冷启动通常发生在第一次打开应用或者系统主动杀掉了你的应用的情况下。和其他启动方式相比,冷启动模式需要系统和应用做更多的初始化操作,所以优化起来也有一定的挑战。

在冷启动的开始阶段,系统需要执行三个任务:

  1. 加载和启动应用程序,
  2. 在app启动后立即显示一个空白启动窗体
  3. 创建App进程。

一旦系统创建了应用进程,应用进程就会执行下面步骤:

  1. 创建应用程序对象
  2. 启动主线程
  3. 创建Main Activity
  4. 初始化构造view
  5. 在屏幕上布局。
  6. 执行初始化绘制操作

一旦 应用进程完成了首次绘制,系统进程会用Main Activity 来替换 之前已经生成的背景窗体。这个时候,用户就可以使用app了。

图1 展示了启动过程中系统和应用程序之间的交替执行过程

图1 冷启动过程重要步骤展示

启动性能问题可能会出现在应用创建和主Activity 创建过程中。

Application 创建

当你的应用启动时,空白窗口将会一直存在 直到系统完成了应用的首次绘制操作,此时,系统会替换掉启动窗口,好让用户能够和App进行交互。

如果你覆写了Application.onCreate()方法,系统将会调用你的Applcation 的onCreate()方法。之后 应用程序将会创建出主线程(也叫UI 线程),并执行创建主Activity 的过程。

从这个时候开始,系统和应用程序级别的进程将按照应用程序生命周期阶段进行。

Activity 创建

当应用进程创建了Activity 后,Activity 会执行以下操作:

  1. 初始化值
  2. 调用构造方法
  3. 调用当前生命周期的回调函数,例如Activity.onCreate()

通常情况下,onCreate()方法对加载时间影响最大,因为它要执行的操作更加繁重:加载和构造view,还有初始化activity运行 所需的对象。

热启动

应用的热启动和冷启动相比,更加简单,开销也更少。在热启动过程中,系统要做的只是把你的Activity切换到前台来。如果应用的所有Activity都驻留在内存中,那么应用就可以避免重复进行对象初始化,布局加载还有渲染。

然而,如果系统执行了内存回收操作并触发了回收事件,例如 onTrimMemory() ,那么热启动时对象仍需要重建。

在屏幕窗口的操作上,热启动操作具有和冷启动的一样的过程:
系统进程将会一直显示一个空白屏幕直到应用完成主Activity的渲染。

暖启动

暖启动过程包含了冷启动过程的部分步骤,同时开销比热启动更小。
暖启动发生在这么几个场景下:

  1. 用户退出了应用,然后又重新打开。这种情况下,应用进程可以继续运行,但应用必须重建Activity 通过调用 onCreate()。
  2. 应用程序内存被系统回收, 然后用户又重新打开。这个时候,进程和Activity都需要被创建,但应用可以从Activity 的onCreate()方法的Bundle类型参数中拿到系统为我们保存的实例。

发现并定位问题

Android 提供了多种方式能让你能够发现并定位App的问题。Android vitals 可以给出问题告警,然后诊断工具可以帮你定位出问题。

Android vitals

Android vitals 可以通过Play控制台提醒您应用的启动时间过长,从而帮助您提高应用的性能。Android vitals启动时间过长的判断标准如下:

  1. 冷启动花费5秒或更长。
  2. 暖启动花费2秒或更长。
  3. 热启动花费1.5秒或更长。

daily session 指的是你App当天的启动情况。

Android vitals 并不会报告热启动的数据。关于Google Play 如何搜集Android vitals 数据的详细信息,请参考 Play Console 文档。

测量慢启动耗时

为了正确评估应用启动性能,你可以关注一些能够显示应用启动时长的数据指标。

初始化显示耗时

在Android 4.4(API level 19)和更高的版本中,logcat包含了一个名为Displayed值的输出行。这个值代表了从应用进程启动到完成Activity绘制所花费时间。这个过程包含了以事件序列:

  1. 启动应用进程,
  2. 初始化对象
  3. 创建和初始化Activity
  4. 构建布局
  5. 首次绘制应用

日志大概长这个样子:

1
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

如果你从命令行或者终端中追踪logcat 输出,那么很容易能够找到这个时间值。
注意你要禁用logcat过滤,因为这条日志是是系统输出的而不是App自己。设置好后你就能很容易抓取到输出的时间值。图二显示了如何禁用过滤器,并且在日志的倒数第二行可以看到Displayd字段的值。

图2 禁用过滤器后输出了Displayed 字段

Display的值 并不一定是所有资源都加载完成后显示的总耗时,它b并不包括不被布局文件及初始化对象所引用的资源的加载时间,因为这个加载过程是一个内部过程,不阻塞应用初始内容的显示。

有时候,Displayed所在的那行输出 包含了一个附加字段total time ,例如

1
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

这种情况下,第一个时间值表示绘制出第一个可见Activity的耗时.后面的total time 指从应用进程的启动开始,可能会包含另一个Activity的启动,但这个Activity 并不可见。total time 只会在启动单个Activity时长和总启动时长不一样才显示。

你也可以使用 ADB Shell Activity Manager 测量启动到显示的时间。像这样:

1234
adb [-d|-e|-s <serialNumber>] shell am start -S -Wcom.example.app/.MainActivity-c android.intent.category.LAUNCHER-a android.intent.action.MAIN

Displayed 依旧会之前那样在logcat中输出,同时你的终端窗口也会有以下输出:

123456
Starting: IntentActivity: com.example.app/.MainActivityThisTime: 2044TotalTime: 2044WaitTime: 2054Complete

-c-a参数是可选的,你可以定义Intent的<category><action>

完全显示耗时

你可以使用 reportFullyDrawn()方法来测量应用启动到所有资源和视图层次结构的完整显示之间所经过的时间,该方法在应用使用延迟加载的情况下是很有用的。在延迟加载时,应用在初始的绘图之后,异步加载资源,然后更新视图。

如果由于延迟加载,应用的初始显示并不包括所有的资源,则可以将所有资源和视图的加载和显示视为单独的度量标准:例如:你的用户界面可能已经完成了文本的加载,但又必须从网络获取图像。

为了解决这个问题,你可以手动调用reportFullyDrawn(),让系统知道你的 Activity 何时完成了它的延迟加载。当您使用此方法,logcat 将显示出从创建应用对象到调用 reportFullyDrawn() 方法的时间。下面是 logcat 的输出示例:

1
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

logcat 输出有时候可能会包含一个total time 字段,这个我们在 初始化显示耗时部分已经讨论过。

如果你确定出你的显示耗时要比预期的长,你进一步继续尝试找出启动过程中的性能瓶颈。

确定性能瓶颈

Android Studio提供了两种方法来确定性能瓶颈,Method Tracer工具和内嵌追踪。 要了解Method Tracer,请参阅该工具的文档

如果您无法访问Method Tracer工具,或者无法在正确的时间启动该工具以获取日志信息,则可以通过在应用程序和Activity的onCreate()方法中进行内嵌跟踪来获得类似的观察结果。 要了解内嵌跟踪,请参阅Trace函数以及Systrace工具的文档。

常见问题

本节讨论经常影响应用程序启动性能的几个问题。主要是关注应用与 Activity 对象的初始化以及画面的加载。

App初始化开销大

Application 的创建过程中,如果执行复杂的逻辑或者初始化大量的对象,将会影响应用的启动体验。具体来说,就是你继承了 Application 并在初始化时执行了不必要的代码。有些初始化可能是完全不需要的,比如:保存 一个实际上由 intent 启动的 app 中的 main activity 的状态信息,通过Intent启动Activitiy,应用只会使用到已经初始化过得状态数据的一部分。

其它在 app 启动期间影响性能的操作还有垃圾回收,或者同步磁盘 I/O,这都会阻塞初始化进程。垃圾回收只是针对于 Dalvik 虚拟机来说,Art 虚拟机同步执行垃圾回收,将影响降到最低了。

诊断问题

可以使用Method tracing或inline tracing来诊断问题。

Method racing

运行Method Tracer工具,会显示出callApplicationOnCreate()方法最终调用`com.example.customApplication.onCreate方法。如果该工具显示这些方法需要很长时间才能完成执行,那么你应该好好看看这些方法到底做了哪些操作。
Inline tracing
使用内嵌追踪来找出可能的引发问题的元凶,包括:

  1. 应用的初始onCreate()函数。
  2. 应用初始化的任何全局单例对象。
  3. 任何瓶颈期间可能发生的磁盘I/O,反序列化,或紧凑的循环操作。

解决方案

不论问题是否由非必要的初始化或磁盘I/O引起,解决方案都是要延迟初始化对象:仅初始化那些立即使用的对象。例如,采用单例模式而不是创建全局静态对象,在该模式中,应用程序仅在第一次访问时才创建对象。也可以考虑使用 Dagger 之类的依赖注入框架,当第一次被注入时才创建对象和依赖。

Activity初始化操作复杂

Activity 的创建通常包含大量高负载工作。一般来说,可以通过考虑以下方面的问题来优化工作提高性能:

  1. 加载比较大或复杂的布局;
  2. 磁盘或网络 I/O 阻塞屏幕绘制;
  3. 加载和解码 bitmap;
  4. 调整 VectorDrawable 对象的大小;
  5. activity 中子系统的初始化。

问题诊断

同样,使用方法追踪和内部追踪。

方法追踪
当运行 Method Tracer 工具时,主要的关注点是 app 中 Application 子类的构造方法和 com.example.customApplication.onCreate() 方法。
如果工具显示完成执行时间较长,那么应该认真查找这部分做了哪些工作。

内部追踪
app 初次 onCreate() ;
app 初始化的任何全局单例对象;
任何在瓶颈期可能形成的磁盘I/O,反序列化或紧凑循环。

解决方案

有很多可能的瓶颈,两个常见的问题和解决方法如下:

  1. view 的层级越多,布局的时候就会花更多时间,可以通过两个步骤优化:

    1. 通过减少重复和布局嵌套是布局变得扁平化。
    2. 不要加载启动时对用户不可见的布局,可以使用 ViewStub。
  2. 把所有资源的初始化放到 main thread 中也会减慢启动速度,可以这样优化:
    1. 使用懒加载或非 main thread 初始化资源。
    2. 允许 app 先展示 view,然后再更新 bitmap 或其它可见资源。

设置启动页的主题

你可以让 app使用自定义主题加载,使 app 的启动屏幕与其它界面保持主题一致,而不是使用系统主题,这样做也可以使 activity 的启动看起来没那么慢。
通常实现启动屏幕主题化的方法是使用 windowDisablePreview 属性去关闭初次启动时的白屏。然而,使用这种方法会导致更长的启动时间,并且,当用户点击启动图标后可能会因为无界面反馈而使用户感到疑惑。

问题诊断

通常可以通过观察 app 启动时是不是没有响应来确定是不是存在问题,这种情况下,屏幕仿佛被冰冻一样卡住,对输入事件也不会有响应。

解决方案

相比于禁用 preview window,可以遵循 Material Design 模式。可以使用 acitivity 的 windowBackground 属性为启动屏幕提供一个简单的背景。
例如,可以创建一个 xml 布局文件:

12345678910
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">  <!-- The background color, preferably the same as your normal theme -->  <item android:drawable="@android:color/white"/>  <!-- Your product logo - 144dp color version of your app icon -->  <item>    <bitmap      android:src="@drawable/product_logo_144dp"      android:gravity="center"/>  </item></layer-list>

对应的 Manifest 文件:

12
<activity ...android:theme="@style/AppTheme.Launcher" />

最简单的切换为正常主题的方式是在调用 super.onCreate()setContentView() 前调用 setTheme(R.style.AppTheme)

12345678910
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);    // ...  }}

原文:大专栏  Android优化App启动时间

原文地址:https://www.cnblogs.com/petewell/p/11584717.html

时间: 2024-08-13 20:00:39

Android优化App启动时间的相关文章

Android 优化APP 构建速度的17条建议

较长的构建时间将会减缓项目的开发进度,特别是对于大型的项目,app的构建时间长则十几分钟,短则几分钟,长的构建时间已经成了开发瓶颈,本篇文章根据Google官方文档,加上自己的一些理解提供一些提升app构建速度的优化建议. 1,为开发环境创建一个变体 有许多配置是你在准备app的release 版本的时候需要,但是当你开发app的时候是不需要的,开启不必要的构建进程会使你的增量构建或者clean构建变得很慢,因此需要构建一个只保留开发时需要配置的变体,如下例子创建了一个dev和prod变体(pr

Android计算app启动时间之二

采用一中的方法计算启动时间,输入adb shell am start -W apk/apk.MainActivity 会报没有权限的错误,google了下,要修改某些东西,懒的改了. 现在来讲下第二种方法,我们可以直接查看日志:如图: 当然有的app首次安装启动会停在引导页,有的app未登录会停在登录界面,这些也可以通过日志查看启动时间.

【Android端 APP GPU过度绘制】GPU过度绘制及优化

一.Android端的卡顿 Android端APP在具体使用的过程中容易出现卡顿的情况,比如查看页面时出现一顿一顿的感受,切换tab之后响应很慢,或者具体滑动操作的时候也很慢. 二.卡顿的原因 卡顿的原因可能有很多种,比如: 1.CPU过高 2.内存溢出 3.主线程处理IO操作等 - 其中过度绘制,是一个容易被忽视但也最好修改并且能够看到效果的内容,其中Android官网给出的过度绘制相关内容见:https://developer.android.com/topic/performance/re

优化 App 的启动时间

这是一篇 WWDC 2016 Session 406 的学习笔记,从原理到实践讲述了如何优化 App 的启动时间. App 运行理论 main() 执行前发生的事 Mach-O 格式 虚拟内存基础 Mach-O 二进制的加载 理论速成 Mach-O 术语 Mach-O 是针对不同运行时可执行文件的文件类型. 文件类型: Executable: 应用的主要二进制 Dylib: 动态链接库(又称 DSO 或 DLL) Bundle: 不能被链接的 Dylib,只能在运行时使用 dlopen() 加载

Android之App应用启动分析与优化

前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动      当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动.冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量.布局.绘制),最后显示在界面上. 2.)热启动      当启动

如何优化app,看Facebook如何做

周四,Facebook Engineering blog 发表了一篇名为<Improving Facebook on Android>博文.博文从四个方面(Performance,Data Efficiency, Networking,Application Size)讲述了Facebook是如何优化app保证其在不同国家不同类型Android设备上都能表现出良好性能的.由于原文内容比较 容易理解,这里就直接给出原文,以使上边链接打不开的同学也能看到.<菜鸟成长史:http://blog

Android Wear - App Structure for Android Wear(应用结构)

原文地址:http://developer.android.com/design/wear/structure.html 用户习惯于点击图标来启动应用程序,但是Android Wear不一样.一个典型的Wear应用程序会在一个情境的合适时刻插入一张卡片到信息流中.这张卡片可能会包含一个用于快速交互的按钮来打开一个全屏视图(在一些情况下,卡片也可能不会提供交互按钮): 以下是简单排序的构建模块.你可以使用其中的一个或者多个模块,但是我们强烈推荐不要构建这样的应用:用户在启动或者退出应用之前必须仔细

Android优化——UI优化(一)优化布局层次

优化布局层次 1.避免布局镶嵌过深(如下) <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent&quo

Android中app卡顿原因分析示例

在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题.后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里.有编程问题可以在这里交流.知乎链接. ========================================================= 我来说下我所知道的事情.我不知道iOS为什么流畅,但我知道一些Android为什么不流畅的原因. 首先,就题主所说的问题,我用iPad和小米Pad对比了一下微博滑动滚屏这件事情(2014年8月10日目前微博