Android 优化Android开发程序员

应用启动速度(Launch-Time)的优化

2018-01-21  本文已影响104人  人海中一只羊

序言

应用启动是整个app工序的第一道流程。对于开发者,一般需要在应用启动过程中进行初始化工作,启动页的UI展示。而对于用户来说,启动速度的快慢则极大地影响了使用体验,并且间接地影响了用户的留存率(对于某些追求流畅体验的用户,一个启动缓慢的app会造成卡顿迟滞的印象,被直接划掉也不是不可能),下图是经过优化的沃家视频启动页.

c21d30343d6a5e2fdbce614b359a86eb.gif

工欲善其事,必先利其器。如果想要对app的启动过程进行优化,那么首先就要了解app的启动过程和常用的优化工具,下面就结合安卓官方文档,以及一些我在预研过程中阅读的优质文章,来总结下app的三种启动过程.


冷启动

冷启动代表app从运存数据完全被擦除的状态启动启动的过程,在此之前,app所属的进程还未被创建.冷启动一般发生在系统重启后或者app被系统杀死后app首次被启动,
冷启动分为以下三个步骤:

在创建完app进程后,则会进行下面几个步骤:

而一旦完成首帧的绘制后,系统会将当前展示的background-window换出,替换为main-activity的背景。从这个时间点开始,用户就可以开始使用app了.

两个重要的时间点
冷启动的总结
image.png

通过上面这张官方文档提供的流程图可以看出,冷启动经过了 Application的创建,Activity的创建,首帧的绘制 等过程,截至到 Displayed-Time这个时间点完成了上述步骤,后面的Other Stuff 步骤可以看做是开发者自定义的一些初始化操作,如果要在log中查看这个时间点,可以通过reportFullyDrawn()这个函数来上报.


热启动

应用程序的热启动要比冷启动简单,消耗也更少,热启动的常见场景就是app的前后台切换.在从后台切换到前台的过程中,如果应用程序的activities还驻留在内存中,app就不需要再重复经历对象初始化,布局加载和渲染这些步骤.
但是,如果某些内存因为内存整理(比如说onTrimMemory())而导致被清理,那么在响应热启动事件时这些被清理的对象就需要重新创建.
热启动和冷启动在屏幕表现上一致,在app完成activity的渲染之前都会一直展示空白屏幕.


温启动

温启动这个名词平时不常见到,官方文档中是这样解释的:温启动包含了冷启动的一部分操作集,同时它的消耗要比冷启动要少.温启动的常见场景如下:


统计应用启动时间

总结了应用启动的几种状态,接着总结如何统计应用启动的时间,因为应用启动的时间一般很短,需要有精确的统计时间来支撑优化结果.

而统计应用启动时间我们可以通过查看日志来获取相关信息:


查看logcat日志

image.png

上图是查看logcat中 TAG为 ActivityManager的日志得到的启动时间信息.可以看到从冷启动状态打开知乎客户端一共耗费了 +1s627ms(知乎客户端启动页出现白屏的情况,感觉不应该出现这种情况).


adb shell am start

通过adb命令我们可以更加得到更加详细的数据.
使用
adb shell am start -W -S [packageName]/[ActivityPath]

使用这个adb命令,我们可以得到较为详尽的启动统计数据,仍以知乎客户端为例子.

adb shell am start -W -S com.zhihu.android/.app.ui.activity.MainActivity
Stopping: com.zhihu.android
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.zhihu.android/.app.ui.activity.MainActivity }
Status: ok
Activity: com.zhihu.android/.app.ui.activity.MainActivity
ThisTime: 1766
TotalTime: 1766
WaitTime: 1780
Complete

具体的启动数据如上所示,这段贴出的adb命令得到的数据和上方截图中的数据来自相同的一次测试,通过对比可以发现,通过adb shell得到的ThisTime这个值与log信息中的DisplayedTime是相同的.但是我们可以看到有三个时间数据: ThisTime TotalTime WaitTime。那么其它两个时间字段分别代表什么含义呢?


ThisTime TotalTime WaitTime 各自的含义

讲解着三个时间段的含义,不得不提到我看到的一篇质量很高的博文,博主长期负责FrameWork层的性能优化工作,对于启动优化理解很深,我试着依样画葫芦在心里讲解了一遍,发现有很多很深的点我get不到,这里就先贴出博文,不再重复分析了.
http://androidperformance.com/2015/12/31/How-to-calculation-android-app-lunch-time.html

这里罗列下最后的结论(图源为上方博文):


所以,这三个启动时间间的关系为:

总之,TotalTime是我们在开发过程中应该改关注的一个时间值,它基本上可以与应用的冷启动过程挂钩。


如何优化应用的启动时间

总结了统计app启动时间的方法后,就需要思考如何优化app启动时间了.我的思路是:优化app启动实际上就是优化冷启动过程,因为冷启动过程包含了启动过程中的每一步.而从上面启动概念总结和统计方法的总结也可以看出,优化应该分为两步:优化进程创建过程的耗时(在应用层面就是优化Application的生命周期函数内的耗时操作)以及优化 Activity的创建过程


优化Applicatoin.onCreate()中的耗时操作

一般在Applicatoin的创建过程中,都会做一些初始化的工作,例如:MultiDex.install(),AppManager.init()(这里是伪代码的形式,泛指各种管理类的初始化工作)。在这些初始化的操作中,难免会包含一些文件读写数据库的增删改查参数配置,等等耗时操作,优化这部分逻辑我的思路是:通过延时异步来解决.

Application的优化工作大概就这些,当然因为各个app复杂程度不同,业务类型不尽相同,具体的优化操作肯定会比较深入,这里我这是按照我在工作中进行的优化工作进行的自我总结,关于我的优化过程重点也不在这里。


Activity创建过程的优化

在这次对app的优化过程中,效果最为明显的操作是对启动页(SplashActivity)的优化。通过上面的基础概念的总结可以得出结论:在Activity的 onCreate()onResume() 生命周期中, app的第一个frame其实都还没有绘制出来.但因为Activity的创建过程和调用链相当长,这里先不总结Activity的创建流程.
只需要先知道Activity的布局和渲染过程其实是在 onResume执行之后才开始的.Activity在AMS置于resume状态后,Activity所属的Window会通过WindowManagerImpl,addView()将decorView放入到 ViewRoot中,然后ViewRoot发起 traversal遍历整个view树,进行布局和渲染,最终将画面绘制到屏幕上.
所以说在不影响业务流程的情况下,为了尽快将app的首帧绘制到屏幕上,我们最后将启动页的耗时操作(例如下载广告图)放在首帧绘制完成之后进行.
那么问题来了,如何尽可能捕捉到应用完成首帧绘制的时间点呢?

在我的另外一篇总结 GPU-Rendering Profile分析中分析了android是如何将view绘制到屏幕上的,基本流程就是 Measure - Layout - Draw - GPU渲染,所以可以选择hook这几个时间点,选择合适的时机插入耗时操作,使得耗时操作可以尽量延迟执行.

    @Override
    protected void onResume() {
        super.onResume();
        getWindow().getDecorView().post(mGetSplashDataRunnable);
       getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
                getWindow().getDecorView().post(mGetSplashDataRunnable);
                Log.i(Constants.TAG.TAG_TEST_LAUNCH_TIME, "onGlobalLayout");
            }
        });
onResume.jpg
onGlobalLayout.jpg

然后列出两种方案的启动耗时测试,发现使用优化方案的平均启动时间要减少80ms左右.


自定义默认窗口背景,优化用户体验

虽然通延时加载耗时任务能够在一定程度上加快app首帧的显示速度,但是缩短的80ms的启动时间相比较总的耗时(520ms左右)来说,用户体验依旧没有得到明显地提升。
前面总结到,点击桌面上的应用图标,首先为我们展示的是一个空白的默认启动背景,为了避免白屏的出现而影响体验,最有效的方法还是需要修改默认的空白背景,替换为app的默认启动页画面.
具体做法为新建一个 shape.xml,背景使用app的默认启动图

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">
    <item android:drawable="@color/white" />
    <item
        android:drawable="@drawable/default_splash_ad"
        android:gravity="fill" />
</layer-list>

然后建立一个新的主题,并将该主题设为启动页Activity的background。

    <style name="AppSplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:background">@drawable/shape_launch</item>
    </style>

这样就从根本上解决了启动页会产生白屏的情况,当然对于启动速度的优化仍然是必要的,否则长时间卡在启动界面,即使没有了白屏情况,用户体验也会很差.

以上。

上一篇 下一篇

猜你喜欢

热点阅读