Android性能优化——启动篇1:Understanding App Launch Time了解应用启动

开始计时【哔】 你知道应用的启动时间意味着什么吗?如果你的应用启动超出了四分之一秒,用户很可能会关掉它干别的事情去了。【哔】我是 Colt McAnlis。忽视应用在启动期间所做的事情将会让它有很严重的性能问题。

Android系统能够很快的对人们的行为做出反应。比如用户一启动你的应用,Android就会立即显示一个Start Window,它会一直显示直到你的应用完成加载,初始化,开始绘制第一帧。它最常见于第一次启动应用的时候,也有可能发生在其他时候,像Activity之间的切换到前台,或者在用户退出应用之后(再启动),或者是为节省内存在被系统回收了之后(再启动)。

基本上,用户在任何时间从其他地方启动你的应用,你都有机会看到这种行为。关键是如果应用启动太慢用户就会感到无趣而去做其他的事情,甚至时间过长,导致ANR对话框的弹出。这些都会给用户留下不好的印象。

应用启动流程介绍

从技术层面,整个启动过程是这样的:当用户启动了你的应用,系统会做些加载应用信息以及创建单独的应用进程的工作。从这开始系统进程就会显示一个Start Window一直显示到应用开始运行。同时,应用进程会创建Application对象启动主线程。从这开始应用的启动界面将会被初始化,创建,填充布局,最后绘制。只有在应用绘制第一帧之后,系统进程才会将Start Window置换为具体界面。

这样的启动过程在多数情况下是很顺畅的,不会发生问题。然而你还是应该留意三个方面的问题。

应用启动的一般性问题

第一件事就是你应该看看Activity在创建时候的是否有过于繁重的处理任务,其中最繁重莫过于填充布局和加载资源。因为布局太过复杂或者有阻塞的逻辑导致创建界面的过程不再简单会是个大问题。

同理,你需要注意下Application的初始化。在复杂的应用中,Application对象的初始过程会生成大量的用于Activity间的全局变量,从而拖慢速度。所以这里就会有两种方式去解决一种是都在这里初始化,方面后面的逻辑会用到,一种是采用延迟加载的方式去解决。

第三,现在很多应用都会自定义一个Start Window,要么是帮助推广应用品牌,要么是看起来像推广应用品牌实际上是延迟加载。如果你是第二种,你应该立即去修复问题而不是掩盖问题;如果是第一种,你就需要考虑在这里设置推广品牌的正确性,不要让用户感到卡顿。

在修复问题之前,你需要知道问题可能出现在哪些地方。幸运的是,有几个工具可以帮到你。

应用启动性能分析工具

第一个Displayed Time输出语句。在Android 4.4版本后,Logcat可以打印从进程启动到界面最终被绘制的时间总和。这很有用,因为它给了你一个应用启动大致的时间。顺便说一下,记着,想要在Android Studio中看到这个值,你需要关掉Logcat的过滤器。

第二是reportFullyDrawn(),在Logcat中的Displayed Time输出语句仅仅适用于非延迟加载的情况,且追踪应用从进程启动到第一次显示的时间大多数情况下是有效的。然而现在的开发过程中,往往采用延迟加载的方式在后台异步加载资源和view,更新view层级,而非阻塞绘制。其结果是被初始化完成的Activity可能已经显示了,但它还没有全部加载完资源,这样系统在评估启动时间性能的时候会将它们分开处理。

第三个方法是Tracing。Display Time和reportFullyDrawn()可以给出一个明确的应用加载时间,方便我们去分析。但是他们没有显示出在整个启动过程中某些部分为什么会慢的具体细节。想要对这一部分深入观察,可以使用Android Studio自带的Tracing工具。

最后就是系统追踪Systrace。当你添加追踪函数在你的onCreate()方法中,它会增加你的输出日志,以便系统追踪Systrace工具能够正确的发现所有的分段记录并以图表的形式显示出来。

附1:官方相关介绍文档

附2:英文演讲字幕

And go. [BEEP]You know what this timer means? Every quarter second a user spends staring at a blank screen instead of interfacing with your app is a quarter second more they’re willing to close your app and give their attention to something else.[BEEP] My name is Colt McAnlis, and misunderstanding all the complex things that happen during your app startup can lead to some serious performance problems.

Now, see, Android is pretty smart when it comes to understanding human performance perception. As soon as the user launches your application, Android will immediately display a start window, which will stay around until your application is fully loaded, initialized, and can draw its first frame. This behavior is most often seen when your app is booted for the first time, but it can easily happen other times as well,like when the activity is brought to the foreground, or after the user backs out of your app, or after some portion of your app has been purged by the system to save memory.

Basically, any time the user moves from something else to your application, there’s a chance you can see this type of behavior. The important point here is this. Letting the user spend too much time looking at the start window gives them ample opportunity to get bored and move on to other things.And taking too long in general could even cause the Application Not Responding dialog to pop up.Neither of these are very good for user retention.

So from the technical side, the whole process works something like this. When the user launches your app, the system does a bit of work to load your application information and create a unique process for your app. From there, the system process will display the starting window and basically hang out until the application is up and running. Meanwhile, the application process will create the application object and launch the main thread. This is where your startup activity will be initialized, created, inflated, and finally drawn. It’s only at this point, after the application has drawn its first frame, that the system process then goes and swaps out the start window for the application.

Now, to be clear, the majority of that entire process happens pretty cleanly. There’s not really much chance that performance can go off the rails. However, there are three big areas where things could become problematic that you should keep an eye on.

The first thing you should really take a look at is all the work that goes into creating your activity class.Most often, there’s lots of heavy lifting that occurs during this process, but heaviest has to be the inflation of layouts and loading of resources that goes along with it.This is not a cheap process, and if you layouts are too complex or you’re got some blocking logic in there, this can cause some really big problems.

On a similar note, make sure you take a look at your application initialization. For really complex apps, the initialization of the app object often becomes a junk drawer for lots of global classes that might be used between activities.So there tends to be lots of work here that could be deferred to later times or perhaps loaded in a lazy-load fashion.

Now, there’s lots of applications out there which provide custom start windows. This is either done to help brand the application or to make a slow load look like a custom-branded application.Now, if you’re doing this to hide bad load times, obviously, you should fix that first. But if you’re doing it for branding, then you need to be aware that there’s a right and a wrong way to set this up, so that it doesn’t influence the user perception too negatively.

But before you run off into the weeds and try to fix these types of common patterns, you need to sit down and figure out if you have a problem in the first place. Thankfully, Android has a few tools to help.

Firstly is Displayed Time. For releases after KitKat, Logcat will include an output line which displays the amount of time between when the process was launched and the activity finally drawn to the screen. This can be helpful, because it gives you a general idea how long it takes to occur for your application. Oh, by the way, note, if you want to see this value inside of Android Studio, you need to turn off filters for the Logcat output. So keep that in mind.

Secondly is reportFullyDrawn function. The displayed metric that’s reported in Logcat is useful most situations where you’d like to track down the time it takes to go from application start to first visible. However, in modern application development, there’s often a great deal of lazy loading-that is, rather than blocking the initial drawing of the window, asynchronously loading resources and views in the background and updating the view hierarchy accordingly. The result is that while the initial activity may be visible,it may not yet be fully loaded with respect to resources, which could be considered a separate metric to use when evaluating launch time performance.To address this concern, you can manually call the activity.reportFullyDrawn function to let the system know that your activity is finished with its lazy loading.

Third is method tracing. While display time and reportFullyDrawn give a good understanding of the overall load time of your application, they do not provide details into what may be causing particular parts of that pipeline to go longer than expected. To gain more insight in this area, you can use the start method tracing tool inside of Android Studio.

And finally is the big one, Systrace. When you add trace functions inside of your onCreate methods, it will augment your logging, such that the Systrace tool can properly discover all the subsections and display them in its graphing process.