Android性能优化之启动优化

4,893 阅读6分钟

一、前言

APP优化是我们进阶高级开发工程师的必经之路, 而APP启动速度的优化,也是我们开启APP优化的第一步。 用户在使用我们的软件时,交互最多最频繁的也就是APP的启动页面,如果启动页面加载过慢,很可能造成用户对我们APP的印象过差,进而消耗了用户的耐心,更严重可能导致用户的卸载行为。这也是微信始终坚持使用“一个小人望着地球”作为启动页面的背景,并且坚持不添加启动广告的的原因。

二、APP的三种启动方式

来看一下Google官方文档《Launch-Time Performance》对应用启动优化的概述;

应用的启动可以分为冷启动,热启动和温启动,而启动最慢、耗时最长的就是冷启动。

冷启动(cold start)

当应用启动时,后台没有该应用的进程(常见如:进程被杀、首次启动等),这时系统会重新创建一个新的进程分配给该应用。

热启动(hot start)

这种启动会从已有的进程中来启动应用,通俗来讲就是已经启用的应用,通过back键或者home键回到系统主界面,再次通过最近任务重新打开Activity的过程。开销比冷启动更小。

暖启动(warm start)

暖启动产生的场景很多。常见如:1。用户使用返回键退出应用,然后马上又重新启动应用。2.应用被内存清除,再次打开应用,会通过OnCreate()中保存的实例状态恢复。

冷启动是从头开始启动APP,而其他两种启动方式是从后台活动返回到前台的一个过程。热启动和暖启动没有明显的区分界限,我们姑且把热启动和暖启动统称为热启动。开发中我们更多的关注冷启动优化,本文也是从Android的实例从发,分析冷启动的启动过程,并给出冷启动的优化方案。

三、APP的冷启动过程

冷启动开始时,系统会依次执行三个任务去启动APP:

  • 加载和启动应用程序
  • APP启动后,立即创建一个空白的启动Window
  • 创建APP的进程

在这三个任务执行后,系统创建了应用进程,那么应用进程接下来会执行下一步:

  • 创建APP对象
  • 开启一个主线程
  • 创建启动页的Activity
  • 加载View
  • 布局view到屏幕
  • 进行初始绘制显示视图

当应用进程完成初始绘制之后,系统进程用启动页的Activity来替换当前显示的空白Window,这个时刻用户就可以使用App了。

四、APP启动时间

APP的启动时间是我们可以检验优化效果的依据,启动时间是指打开应用从初始化到显示启动页Activity的这一段时间。 Google官方的解释:APP startup time

使用过logcat查看启动时间

在Android4.4(API level 19)以上的Android版本上,当启动应用时Android Studio自动会在logcat中输出启动时间。 这个时间从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止。

如Display显示:

I/ActivityManager: Displayed com.example.app/com.example.app.SplashActivity: +1s742ms (total +49s450ms)

reportFullyDrawn()方法

Activity 的reportFullyDrawn()方法, 它会在Logcat里打印从apk初始化到reportFullyDrawn()方法被调用用了多长时间(文章的reportFullyDrawn()SplashActivity中的onCreate()中执行,可以看到显示的时间和Displayed的是一摸一样的)

I/ActivityManager: Fully drawn com.example.app/com.example.app.SplashActivity: +1s742ms (total +49s450ms)

执行adb命令手动查看启动时间

adb shell am start -W [packagename]/[packagename.SplashActivity]

输出:

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.app/.SplashActivity }
Status: ok
Activity: com.example.app/.SplashActivity
ThisTime: 1368
TotalTime: 1368
WaitTime: 1432
Complete

ThisTime:最后一个启动的Activity的启动耗时;

TotalTime:自己的所有Activity的启动耗时;

只用关注TotalTime就行了

启动时间标准

官方给出,当启动时间超出以下指标时,会被认为启动时间过长,这是就需要考虑仔细优化启动时间。

  • 冷启动时间超过5s
  • 热启动时间超过1.5s
  • 暖启动时间超过2s

说了一大堆启动的基础知识,下边开始讲解真正的优化实战

五、启动优化实战

解决应用刚启动时的白屏问题

前边讲到,应用初始化会进行一系列进程的创建,资源的初始化工作,这段时间系统会先分配一个空白的Window,这会造成用户打开应用到显示第一个可交互的Activity,会经历一段白屏的时间。

这时我们可以在给app定义一个主题去解决,在Activity显示出来之前先显示一个主题背景,去填补空白的Window阶段。

定义一个Splash主题

    <!--Splash launcher-->
    <style name="LauncherTheme" parent="AppTheme">
        <item name="android:windowBackground">@mipmap/ic_splash_bg</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>

manifest中引用该主题

 <activity android:name=".activity.SplashActivity"
            android:theme="@style/Launcher">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

最后记得在启动页显示以后恢复默认的APP主题

  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setTheme(R.style.AppTheme)
        setContentView(R.layout.activity_splash)
      
  }

这种方法只是视觉上给用户一种快速启动的感觉,不能减少实际的启动时间。

避免Application初始化过重

随着我们工程越做越大,第三方库和组件也逐渐被依赖到我们的项目中,避免不了会在Application的onCreate()中执行很多第三方库的初始化工作。大量的初始化工作导致该生命周期过于沉重,可能会加长应用的启动时间,因此我们应该对这些第三方库进行分类和优化。

  • 必须在onCreate()且是主进程中初始化
  • 可以延迟,但是需要在Application中初始化
  • 可以延迟到启动页的生命周期回调中初始化
  • 延迟到用的时候再初始化

大家可以根据自身项目去整理代码,可以延迟执行的应该放在IntentService或者Work Thread中进行初始化。

  • 例如EventBus 需要在Activiy中使用的,必须在Application中初始化
  • 例如Bugly ,GrowingIO等类似库的可以放在Work Thread中初始化
  • 例如地图定位、ImageLoad可以延迟到使用之前初始化
  • SplashActivity中网络加载的资源,可以首次加载存放在缓存中,下次启动的时候再显示
  • 注意有些第三方库必须在主线程中初始化
  • 避免耗时操作,如数据库I/O操作不要放在主线程执行
  • 删除无用或重复的代码
  • 减少首屏Activity中的网络请求密度