背景

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

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

冷启动

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

加载并启动应用。

再启动后立即显示应用的空白启动窗口(不做优化时的白屏现象)。

创建应用进程。

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

创建应用对象。

启动主线程。

创建主 Activity。

扩充视图。

布局屏幕。

执行初始绘制。

把当先显示的后窗口(即前面提到的白屏窗口)替换为主 Activity,回调生命周期方法。

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

温启动

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

热启动

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

优化分析测量工具

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

package="me.tandeneck.launchtime">
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">
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 情况下,而且测试手机性能也算中规中矩。

为了应对这个问题,有些同学会用透明主题来解决,如下:

true

true

我们简单来看下效果:

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

style 设置为:

true
true
@drawable/bg

然后设置 SplashActivityActivity 的启动 Theme:

android:theme="@style/SplashTheme">

效果如下:

可以看到,首先显示的是在主题中设置的背景图,然后是 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 键的效果。


android 优化 应用 安卓优化应用 无法启动_android