Android 优化之 App 启动优化

背景

前面我们分析了 App 启动流程分析(基于 Android 10) ,这次我们一鼓作气,来撸一撸 App 启动优化,本文主要就一些常规手段做一些梳理,毕竟不同的 App 要优化的目的会有一些不同和侧重。

应用启动类型(冷启动、温启动、热启动)

冷启动

冷启动是指应用从头开始启动,冷启动开始后,系统会做以下事情:

  1. 加载并启动应用。
  2. 再启动后立即显示应用的空白启动窗口(不做优化时的白屏现象)。
  3. 创建应用进程。

创建应用进程可分为以下阶段:

  1. 创建应用对象。
  2. 启动主线程。
  3. 创建主 Activity。
  4. 扩充视图。
  5. 布局屏幕。
  6. 执行初始绘制。
  7. 把当先显示的后窗口(即前面提到的白屏窗口)替换为主 Activity,回调生命周期方法。

冷启动在几种启动类型中最慢,一般我们做启动优化大部分工作也是消耗在这里。

温启动

温启动比冷启动的效率高一点,比如说用户退出应用后(不是按 Home 键退后台),马上又打开 App,这时候进程大概率会继续运行,即免去了创建进程那一步,而直接创建主 Activity 并回调生命周期。

热启动

热启动在这三种启动类型中开销最低,一般来说就是应用的 Activity 都还驻留在内存中,应用无须再创建 Activity 实例,布局的绘制和呈现。一个比较的场景是用户按 Home 键然后在系统杀死进程前重新进入 App。

优化分析测量工具

我们首先创建一个空项目,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.tandeneck.launchtime">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SplashActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
class SplashActivity : AppCompatActivity() {

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

首先要明确启动优化的目的是使我们的应用启动速度更快,让用户所点即所达,解决问题的前提是找出问题,因此我们要确定启动的时间,在哪里耗时严重,下面就介绍几种常用的方法来帮助我们找到耗时的原因。

1. Logcat 中 筛选 Displayed 关键字

这个 Displayed 的值代表从启动进程到第一个 Activity 完成绘制所经过的时间。如下图:创建一个空项目并运行,发现启动需要 462 ms,当然这个值每一次都会有点差异的:

2. 使用 ADB Shell Activity Manager 命令运行应用测量显示时间,命令如下:
adb shell am start -S -W [packageName]/[ packageName. AppstartActivity]

-S 表示在启动 Activity,强行停止目标应用,-W 表示等待启动完成,更多信息有兴趣的同学可以查看 adb 命令

在 Terminal 运行上面命令可得到以下信息:

可以发现 LaunchState 为 COLD,代表的是冷启动,还有 TotalTime 和 WaitTime,可以发现 TotalTime 和 Diapalyed 打印处理的值是一样的,而 WaitTime 会大点,因为它会把系统初始化的一些工作时间算进去,而这部分的时间我们是比较难进行优化的,因此我们关注 TotalTime 即可。

前面这两种方法可以帮助我们对 App 启动的速度有个概览,但是具体到哪个方法耗时,哪个调用时间长我们是无法得知的。下面就介绍一些检测耗时操作的方法。

3. 代码埋点

乍听起来好高大上的感觉,其实就是打印日志,在方法执行开始前获取当前时间,再在方法结束后获取时间,两个时间相减即可得到方法执行时间,相信大家都有试过这种方法。这样会写很多样板代码,可以引入第三方库比如 JakeWharton 的 Hugo 来减少样板代码,这个库可以通过注解的方式获取执行时间,主要运用了 AOP 技术,不过这个库有点年头了,有兴趣的同学还是可以去了解下的。

4.使用 TraceView、SysTrace 等性能分析工具

TraceView 和 SysTrace 这些都是分析性能的神器,由于篇幅原因,这里不做具体展开,有兴趣的同学可以自行了解相关资料,或者期待下我后续的博文。

优化手段

1. 启动窗口优化

前面也提到过,默认情况下会启动一个空白窗口,如下图:

白屏窗口虽然短暂,但是还是可以明显感知它的存在,这还是在简单的 Demo 情况下,而且测试手机性能也算中规中矩。
为了应对这个问题,有些同学会用透明主题来解决,如下:

    <!-- Base application theme. -->
   <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
       <item name="android:windowFullscreen">true</item>
       <item name="android:windowIsTranslucent">true</item>
   </style>

我们简单来看下效果:

哎?空白窗口消失啦,不过细心的同学会发现点击后会有点延时,这是因为我们设置的透明主题起作用了,即我们之前看到的白屏窗口现在是透明的,所以会延迟。这时,如果我们可以把透明主题替换为闪屏页图片,比如下面这张图:

style 设置为:

    <style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowBackground">@drawable/bg</item>
    </style>

然后设置 SplashActivityActivity 的启动 Theme:

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

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

效果如下:

可以看到,首先显示的是在主题中设置的背景图,然后是 SplashActivity (下面的那行文本其实是SplashActivity的布局文件),最后是 MainActivity,整个流程是这样:

启动页主题背景图在某些手机会出现拉伸的情况,可以通过使用点 9 图或者使用 layer-list 来解决。

2. Application 优化和主 Activity 优化。

以上的这种优化只是视觉优化,并不能真正减少用户的启动时间,用大白话说就是 Application 和 主 Activity 创建的时间该是多少就是多少。实际项目中并不会像 Demo 这样只是一个空壳 Applicaiton 和 Activity,比如在 Application 中会做许多繁重的初始化操作。这种情况下就要结合业务要进行优化啦,首先通过工具比如 Systrace 获取耗时的函数,然后对耗时的函数进行优化,如果放在子线程中加载不影响业务的情况,则优先选择放在子线程中加载。主 Activity 的优化同理,不过主 Activity 涉及到界面,还可以从页面优化的方向着手,无非就是减少冗余或者嵌套的布局来减少底视图层次结构,用 ViewStub 替代在启动过程红不需要显示的 UI 控件。

3.类重排

类重排的实现通过 ReDex 的 Interdex 调整类在 Dex 中的排列顺序,把启动时需要加载的类按顺序放在主 dex 里。具体实现可以参考 Redex 初探与 Interdex:Andorid 冷启动优化

4.减少冷启动的次数

从前面得知,冷启动的耗时是最长的,因此我们可以在用户非主动退出应用的情况下不再退出进程。在我们的 Activity 栈底 activity (一般是 MainActivity)加入以下代码:

    override fun onBackPressed() {
//        super.onBackPressed()
        moveTaskToBack(true)
    }

moveTaskToBack 的作用是把 Activity 隐藏在后台,相当于触发 Home 键的效果。

5. 其他一些常规优化
  • \color{red}{线程优化},通过减少 CPU 调度带来的波动,让启动时间更稳定,如果启动过程中有太多的线程一起启动,会给 CPU 带来非常大的压力。
  • \color{red}{GC 优化},启动过程中减少 GC 的次数。如避免进行大量的字符串操作,特别是序列化和反序列化;如避免重复创建对象,出现这种情况,可以考虑复用。
  • \color{red}{IO 优化},启动过程 IO 负载较高,尽量较少网络 IO 和磁盘 IO。
  • \color{red}{内存优化},启动的应用如果需要大内存,而这时如果没有足够的内存,那么系统必须要通过杀死其他应用的方式来满足这个 App 内存的需要,这个过程中会对内存进行频繁的操作,导致启动速度变慢。

总结

App 启动优化除了以上的优化手段外,还有许多手段由于篇幅原因未能一一进行详细说明,比如保活、预加载等。

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342