App 启动优化

personal

许多应用都出现启动速度缓慢,出现黑屏,白屏问题,这是因为 APP 没有进行启动优化。


Google文档

先看看 Google 官方文档 Launch-Time Performance 对应用启动优化的概述:

应用的启动分为冷启动、热启动、温启动,而启动最慢的就是冷启动。

应用在冷启动之时,要执行三个任务:

  1. 加载启动 App;
  2. App 启动时立即展示出一个 Preview Window(预览窗口);
  3. 创建 App 的进程;


而这三个任务执行完毕之后,马上执行以下任务:

  1. 创建 Application 对象;
  2. 创建启动 Activity 对象;
  3. 加载 View;
  4. 布置屏幕;
  5. 进行初始绘制;

而一旦 App 进程完成了第一次绘制,系统进程就会用 Activity 替换已经展示的 Background Window,此时用户就可以使用 App 了。

有上述 APP 启动过程可知,App 进程的创建等环节我们是无法控制的,可以优化的也就是Application、Activity创建以及回调等过程


Google 也给出了优化启动速度的方式:

  1. 修改 Launcher Activity 主题,提前展示预览画面;
  2. 避免在启动时做密集沉重的初始化;
  3. 定位问题:避免I/O操作、反序列化、网络操作、布局嵌套等。

上面三种方式,第一种是用户体验上的提升,第二三种是真实的优化启动速度。


修改主题

App 一般都会有一个 Splash Activity,也就是 APP 启动时的第一个页面,其作用一般是检查更新,初始化程序,宣传公司品牌,以及活动展示。

我们的优化点,就是修改这个 Activity 的 windowBackground 属性,因为如果不指定,系统默认 windowBackground 要么全白要么全黑,导致刚刚启动 APP 时,总能在一瞬间看到这个窗口,体验很差。

具体操作如下:

首先,在 AndroidManifest.xml 中更改 Splash Activity 的主题

     <activity
            android:name=".mvp.ui.activity.SplashActivity"
            android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

然后,在 styles.xml 中指定 windowBackground

    <style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@drawable/splash</item>
    </style>

splash.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 背景颜色 -->
    <item android:drawable="@color/white"/>

    <item>
        <!-- 图片 -->
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_launcher"/>
    </item>
</layer-list>

这种方式其实并没有真正的加速启动过程,而是通过交互体验来优化了展示的效果。


启动加速

现在,我们来真正的加速 APP 的启动,这里关注的就是 Application 以及 Splash Activity 中的初始化代码。

在 Application 中,如果使用了 MultiDex ,那么需要进行对应优化,详情请查看底部参考文章。

其次,在 onCreate() 中,第三方 SDK 会要求在此初始化,如果全部放在主线程,这样的方式肯定是过重的。

解决办法是:异步加载、延时加载、懒加载。

将耗时操作放到子线程,争抢资源的操作进行延时加载或者懒加载。

具体使用哪种方式,这还要根据 第三方SDK 自身情况来决定,这里也不易展开。

我们知道,最常见的解决办法是异步加载,启动时,耗时操作发生在主线程,那是不是多开几个子线程同时操作就行呢?不是。

优化启动不能全都靠着异步来解决问题,错误的使用线程不仅不能改善,反而肯能加剧卡顿。是否需要开启线程?需要开几个线程?这些需要根据具体情况分析性能瓶颈后,才能给出对应的解决方式。

开启线程的方式也同样有区别,Thread、ThreadPool、AsyncTask、HandlerThread、IntentService都各有利弊。分析可知,IntentService 运行在子线程,而且还可以与 Activity 绑定,所以一般会使用 IntentService 来进行一些可以异步的初始化工作。

除了异步,还应该多多考虑,一些初始化是否可以使用延时加载,是否能使用懒加载?

还有一些流程性的事物也可以优化,比如 广告页的下载展示,APP 后台下载升级等。


工具的使用

在优化启动时,我们必须掌握一些分析耗时的基础工具。

1. 使用 ADB

最简单的方式是通过 ADB 查看启动耗时

adb shell am start -W packagename/activity

例如:

➜ adb shell am start -W com.medprin.medprin/com.medprin.medprin.mvp.ui.activity.SplashActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.medprin.medprin/.mvp.ui.activity.SplashActivity }
Status: ok
Activity: com.medprin.medprin/.mvp.ui.activity.SplashActivity
ThisTime: 248
TotalTime: 248
WaitTime: 250
Complete

这里解释一下三个值的含义:

  • WaitTime 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;
  • ThisTime 表示一连串启动 Activity 的最后一个 Activity 的启动耗时;
  • TotalTime 表示新应用启动的耗时,包括新进程的启动和 Activity 的启动。

详细解释,请看Android 中如何计算 App 的启动时间?


如果关心系统启动应用耗时,参考WaitTime;

如果关心应用有界面Activity启动耗时,参考ThisTime。

如果只关心某个应用自身启动耗时,参考TotalTime,开发者一般关注这个;


2.TraceView

使用 adb 的方式太简单粗略了,只能看出启动的总耗时,也看不出具体哪些地方耗时多长,这个时候就需要更加专业的工具,TraceView。

使用非常简单,直接在想要监测位置的开头和结尾,各自调用两个方法

Debug.startMethodTracing("Test");//开始
...
Debug.stopMethodTracing();//结束

运行程序,会在 sdcard 上生成名为 Test.trace 的文件。注意,sdcard 的操作需要添加权限。

然后通过 adb pull 命令 将文件导入到本地后,使用 DDMS 打开 trace 文件, 或者 直接使用 AS3.0之后出现的工具 Devices File Exploer ,直接打开 trace 文件。

一般只需要关注两个值:

  • Cpu Time / Call:反映调用次数不多,但每次调用却需要花费很长时间的函数
  • Calls + Recur Calls / Total:反映自身占用时间不长,但调用却非常频繁的函数


3. Hugo

还有一个是 JakeWharton 的 hugo ,这个库利用 AspectJ 使用注解,可以非常方便的打印方法耗时。

就像这样:

@DebugLog
public String getName(String first, String last) {
  SystemClock.sleep(15); // Don't ever really do this!
  return first + " " + last;
}
V/Example: ⇢ getName(first="Jake", last="Wharton")
V/Example: ⇠ getName [16ms] = "Jake Wharton"

详细集成配置,请查看 GitHub 上文档


参考

Android性能优化(一)之启动加速35%

Android性能优化系列之App启动优化

APP启动优化

Android的几种多线程方式

TraceView 简介及其案例实战

使用 TraceView 找到卡顿的元凶

启动速度优化之耗时检测处理

美团Android DEX自动拆包及动态加载简介

Android MultiDex初次启动APP优化

其实你不知道MultiDex到底有多坑


猜你喜欢

转载自blog.csdn.net/deemons/article/details/80377338