1. 视觉优化
1.1. 基本概念
冷启动时,系统有三个任务:
- 加载并启动应用程序
- 启动后立即显示应用程序空白启动窗口
- 创建应用程序进程
应用程序进程创建后,就会负责下一阶段:
- 创建APP对象
- 启动主线程
- 创建应用入口的Activity对象
- 填充加载布局的Views
- 在屏幕上执行View的绘制过程 measure-layout-draw
完成第一次绘制之后,系统进程会交换当前显示的背景窗口,将其替换为主Activity,此时用户才可以真正使用该应用程序
视觉优化就是在冷启动的第1~2阶段,设置空白启动窗口的主题,来从视觉层面使黑白屏消失,但是对APP启动速度没有任何改善
1.2. 默认情况
如果对APP没做任何处理,即使用默认主题,则会有黑白屏问题。大概是onWindowFocusChanged
回调的时候,启动窗口消失
1.3. 透明主题优化
为Application设置一个透明背景主题,例如
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
虽然没有白屏,但是点击APP图标到首个页面呈现,仍然会有视觉延迟
1.4. 图片主题
为Application设置一个闪屏图片的主题,例如
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/lunch</item>
<item name="android:windowFullscreen">true</item>
<!--显示虚拟按键,并腾出空间-->
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
</style>
这样设置之后,如果闪屏图片和闪屏Activity长得一模一样,就可以无缝衔接。
2. 代码优化
针对视觉优化的治标不治本,根本的解决办法还是在于代码上优化
2.1. 冷启动耗时统计
2.1.1. 方法一:adb
adb shell am start -S -W 包名/类的全路径限定名
,-S表示重启当前应用
dst07130:~ renpeng$ adb shell am start -S -W com.pren.androidtest/com.pren.androidtest.MainActivity
Stopping: com.pren.androidtest
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.pren.androidtest/.MainActivity }
Status: ok
Activity: com.pren.androidtest/.MainActivity
ThisTime: 594
TotalTime: 594
WaitTime: 638
Complete
三个Time的含义:
- ThisTime:最后一个Activity的启动耗时,即命令里面输入的那个Activity(冷启动优化关注)
- TotalTime:启动一连串的Activity的总耗时
- WaitTime:创建应用进程的耗时 + TotalTime(用户感知)
2.1.2. 方法二:Logcat
Logcat 输入“Display”筛选系统日志,不过滤信息“No Filters”,不过滤层级“Verbose”
显示的耗时为ThisTime
2.2. Application优化
很多第三方组件都在Application的onCreact
中抢占先机完成初始化
但在Application中完成繁重的初始化操作和复杂的逻辑就会影响到启动性能例如:
- 复杂且繁琐的初始化布局
- 阻塞主线程的操作,如I/O、数据库等
- 加载Bitmap大图片或者VectorDrawable等
- 其他占用主线程的操作
优化一般分为三类:
- 只能在主线程且必须立即执行——无法优化
- 只能在主线程但不必立即执行——延迟
handler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 3000);
- 可以不在主线程中执行—子线程
new Thread(new Runnable() {
@Override
public void run() {
// 设置线程的优先级,不与主线程抢资源
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 子线程延迟执行,如Bugly、x5、SP、友盟等初始化
// 建议延迟,可以发现是否影响其它功能,或者是崩溃!
Thread.sleep(5000);
}
}).start();
2.3. 闪屏页优化
只能在主线程且必须立即执行无法优化的,则可以增加一个闪屏页面抵消掉它的影响
目标闪屏总时间 = 空白页时间 + 真实闪屏时间
Application初始化完成后会调用attachBaseContext
方法(它是整个APP中回调的第一个方法),再回调Application的onCreate
方法,所以就可以在Application中记录启动时间。
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 可以使用SP,也可以使用全局变量记录开始的时间
long startTime = System.currentTimeMillis();
}
Activity获取用户触摸事件和View绘制完毕(首帧回执完毕,但不代表数据完成展示),会回调onWindowFocusChanged
方法,所以就可以计算APP启动耗时了。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// 从application到入口Acitity的时间
long diffTime = System.currentTimeMillis() - startTime;
// 所以闪屏页展示的时间为 目标闪屏总时间 - diffTime.
}
这样就能动态设置闪屏的显示时间,让不同性能的手机闪屏时间尽量保持一致。