App 启动时间

了解应用程序-启动内部内容

  • 冷起动

冷启动指的是应用程序从头开始:这个开始,系统的进程才创建了应用程序的进程。
冷启动发生在这样的情况下,例如您的应用程序是在设备启动后,首次启动的,或者是在系统关闭应用程序后启动的。
这种类型的启动在减少启动时间方面提出了最大的挑战,因为系统和应用程序要比其他启动状态下有更多的工作要做。在冷启动的开始阶段,系统有三个任务:

  1. 加载并启动应用程序。
  2. 启动后立即显示应用程序的空白启动窗口。
  3. 创建应用程序进程。

一旦系统创建了应用程序进程,应用程序进程将负责接下来的阶段:

  1. 创建应用程序对象。
  2. 启动主线程。
  3. 创建主活动。
  4. inflate view。
  5. 布置视图到屏幕。
  6. 执行初始绘制。

应用程序进程完成第一次绘制后,系统进程将替换当前显示的后台窗口,代之以主活动。此时,用户可以开始使用该应用程序。

图下显示了系统和应用程序如何在彼此之间传递工作:

在创建应用程序和创建活动时,可能会出现性能问题
1、Application创建。
当应用程序启动时,空白的启动窗口将保留在屏幕上,直到系统第一次完成应用程序的绘制。
此时,系统进程将从您的应用程序的启动窗口切换出去,从而允许用户开始与该应用程序进行交互。
如果您在自己的应用程序中重载了Application.onCreate(),则系统将在您的应用程序对象上调用onCreate()方法。
之后,应用程序生成主线程(也称为UI线程),并通过创建主活动来执行任务。
从这一点出发,系统和应用程序级别的流程将按照应用程序生命周期阶段进行。

当然在Application.onCreate()中进行大量初始化操作,就会导致启动慢,性能问题,可把优先级比较高的初始化放在这里,根据需求适合转移初始化位置。
2、Activity创建。
在应用程序流程创建Activity后,该Activity将执行以下操作:
初始化值。
调用构造函数。
调用Activity当前生命周期状态的回调方法,如Activity.onCreate()。
通常,onCreate()方法
对加载时间的影响最大,因为它以最高的开销执行工作:加载和Inflate视图,以及初始化运行活动所需的对象。
 所以,Activity.onCreate()里仅创建必需的对象,减少xml嵌套以及布局相关优化方案

  • 热启动

应用程序的热启动比冷启动简单得多,开销也小得多。
在热启动时,系统所做的就是将您的活动带到前台。
如果应用程序的所有活动仍驻留在内存中,则应用程序可以避免重复对象初始化、布局Inflate和渲染。
但是,如果某些内存已被清除以响应内存处理事件,例如onTrimMemory(),则需要重新创建这些对象以响应热启动事件。热启动与冷启动场景在屏幕上显示的行为相同:在应用程序完成呈现活动之前,系统进程将显示一个空白屏幕。

  • 温启动

温启动包含在冷启动期间发生的一些操作的子集;同时,它代表的开销比热启动少。有许多潜在的状态可以被认为是温暖的开始。
例如:

  1. 用户退出您的应用程序,然后重新启动它。
  2. 进程可能会继续运行,但应用程序必须通过调用onCreate()从头开始重新创建活动。
  3. 系统将您的应用程序从内存中移除,然后用户重新启动它。
  4. 流程和活动需要重新启动,但是任务可以从传递到onCreate()的保存的实例状态包中受益。

诊断缓慢的启动时间。
为了正确诊断启动时间性能,您可以跟踪显示应用程序启动时间的指标。
冷启动需要5秒或更长时间。
热启动需要2秒或更长时间。
热启动需要1.5秒或更长时间。

在Android4.4(APILevel19)及更高版本中,logcat包含一个标签“Displayed”关键字过滤。
此值表示从启动流程到在屏幕上完成相应活动之间的时间量。
经过的时间包含以下一系列事件:

  1. 启动进程。
  2. 初始化对象。
  3. 创建并初始化Activiy。
  4. Inflate布局。
  5. 第一次绘制应用程序。
ActivityManager: Displayed com.xxx.xxx/.SplashActivity: +1s67ms
ActivityManager: Displayed com.xxx.xxx/.ui.MainActivity: +549ms

 logcat输出中"Displayed "不一定会捕获在加载和显示所有资源之前的时间量:它不包括布局文件中未引用的资源或应用程序在对象初始化过程中创建的资源。
它排除了这些资源,因为加载它们是一个内联过程,并且不会阻止应用程序的初始显示。
有时,logcat输出中显示的行包含一个用于总时间的附加字段。
例如:
ActivityManager:显示的com.android.myexample.StartupTiming:+3s534ms(total+1m22s643ms)。
在这种情况下,第一次测量仅适用于第一次绘制的活动。
total测量从应用程序进程开始时开始,可能包括另一个先启动但没有在屏幕上显示任何内容的活动。
仅当单个活动与总启动时间之间存在差异时,才会显示总时间度量。

ActivityManager: Displayed com.xxx.xxx/.ui.user.LoginActivity: +623ms (total +3s832ms)
ActivityManager: Displayed com.xxx.xxx/.ui.MainActivity: +470ms

您还可以通过使用ADB Shell Activity Manager命令运行应用程序来测量初始显示时间。

H:\as_project\BuildingService>adb shell am start -S -W com.xx.xxx/.SplashActivity
Stopping: com.xxx.xxx
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.xxx/.SplashActivity }
Status: ok
Activity: com.xxx.xxx/.SplashActivity
ThisTime: 1467
TotalTime: 1467
WaitTime: 1498
Complete

WaitTime 返回从 startActivity 到应用第一帧完全显示这段时间. 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;
ThisTime 表示一连串启动 Activity 的最后一个 Activity 的启动耗时;
TotalTime 表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用Activity pause的耗时。
开发者一般只要关心 TotalTime 即可,这个时间才是自己应用真正启动的耗时 

经常影响应用程序启动性能的几个问题

主要涉及到初始化Application和Activity对象,以及加载屏幕。

  • 重型Application初始化

当代码重写Application对象并在初始化该对象时执行繁重的工作或复杂的逻辑时,启动性能可能会受到影响。
如果应用程序子类执行不需要执行的初始化,则应用程序可能会在启动过程中浪费时间。
有些初始化可能是完全不必要的:例如,初始化主要活动的状态信息,而应用程序实际上是为了响应某个意图而启动的。
出于某种意图,应用程序只使用先前初始化的状态数据的一个子集。应用程序初始化期间的其他挑战包括有影响的或大量的垃圾收集事件,或者与初始化同时发生的磁盘I/O,从而进一步阻止初始化进程。
垃圾收集是Dalvik运行时特别需要考虑的问题;Art运行时同时执行垃圾收集,最大限度地减少了该操作的影响。
诊断问题。

方法追踪
运行CPU事件探查器显示,callApplicationOnCreate()方法最终调用com.example.customApplication.onCreate方法。如果该工具显示这些方法需要很长时间才能完成执行,那么您应该进一步探索,看看在那里发生了什么工作。

内联跟踪
使用内联跟踪调查可能的罪魁祸首,包括:

  1. 你的应用程序的初始onCreate()函数。
  2. 应用程序初始化的任何全局Singleton对象。
  3. 瓶颈期间可能发生的任何磁盘I/O、反序列化或紧密循环。

解决问题的方法:
无论问题是不必要的初始化还是磁盘I/O,解决方案都需要延迟初始化对象:只初始化那些立即需要的对象。例如,不是创建全局静态对象,而是移到单例模式,在该模式中,应用程序仅在第一次访问对象时初始化对象。此外,请考虑使用Dagger这样的依赖注入框架来创建对象和第一次注入时的依赖关系。

  • 重型Activity初始化

活动创建通常需要大量高开销的工作。
通常,有机会优化这项工作以实现性能改进。
这些共同问题包括:

  1. Inflate比较大的或复杂的布局。
  2. 阻止磁盘上的屏幕绘制或网络I/O。
  3. 加载和解码位图。
  4. 栅格化VectorDrawable对象。
  5. 初始化活动的其他子系统。

在这种情况下,方法跟踪和内联跟踪都很有用。
方法追踪。
使用CPU事件探查器时,请注意应用程序的Application子类构造函数和com.example.customApplication.onCreate()方法。
如果该工具显示这些方法需要很长时间才能完成执行,那么您应该进一步探索,看看在那里发生了什么工作。
内联跟踪。
使用内联跟踪调查可能的罪魁祸首,包括:

  1. 您的应用程序的初始onCreate()函数。
  2. 它初始化的任何全局Singleton对象。
  3. 瓶颈期间可能发生的任何磁盘I/O、反序列化或紧密循环。

解决问题的方法
有许多潜在的瓶颈,但有两个共同的问题和补救办法如下:

 视图层次越大,应用程序Inflate的时间就越长

解决此问题可以采取以下两个步骤:

  1. 通过减少冗余或嵌套布局来展平视图层次。
  2. 不会Inflate UI中不需要在启动过程中可见的部分。
  3. 相反,使用ViewStub对象作为应用程序可以在更合适的时间膨胀的子层次结构的占位符。

将所有资源初始化都放在主线程上也会减慢启动的速度
您可以按以下方式解决此问题:

  1. 移动所有资源初始化,以便应用程序可以在不同的线程上懒洋洋地执行它。
  2. 允许应用程序先加载和显示你的视图,然后更新依赖于位图和其他资源的视觉属性。

启动主题屏幕

您可能希望以应用程序的加载体验为主题,以便应用程序的启动屏幕在主题上与应用程序的其余部分保持一致,而不是与系统主题一致。
这样做可以隐藏缓慢的活动启动。
实现主题化启动屏幕的常见方法是使用windowDisablePreview主题属性关闭启动应用程序时系统进程绘制的初始空白屏幕。
但是,与不抑制预览窗口的应用程序相比,此方法可能会导致更长的启动时间。
此外,它迫使用户在活动启动时等待没有任何反馈,使他们怀疑应用程序是否运行正常。
这样启动APP,虽然没有白屏,但会出现点击桌面图标而半天没有反应,体验不好,很多APP把这个闪屏当做一个广告、品牌宣传的页面处理。

<style name="AppTheme.Launcher">
    <!--关闭启动窗口-->
    <item name="android:windowDisablePreview">true</item>
</style>

 您通常可以通过观察用户启动应用程序时响应缓慢来诊断此问题。
在这种情况下,屏幕看起来可能被冻结了,或者已经停止了对输入的响应。
解决问题的方法。
我们建议您遵循常用的“材质设计模式”,而不是禁用预览窗口。
您可以使用活动的windowBackbase主题属性为开始的活动提供一个简单的自定义绘图。
例如,您可以创建一个新的可绘制文件,并从布局XML和应用程序清单文件中引用该文件,如下所示:
布局XML文件:

<style name="AppTheme.Launcher">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowBackground">@mipmap/app_bg_welcome</item>
</style

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值