Android 5.x 以下加载 MultiDex 白屏的处理优化
当 APP 的 minSdkVersion 低于 Android 5 时,在方法数大于 65536 时,需要将 APP 打包为多个 DEX 文件,此时需要添加 MultiDex 依赖。
官方方法如下:
1.build.gradle
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.3'
}
2.MyApplication
方式❶:
public class MyApplication extends MultiDexApplication { ... }
方式❷:
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
此外,为了避免一些启动期间需要的任何类未在主 DEX 文件中提供而导致java.lang.NoClassDefFoundError
,还需要告诉 AS 将这些类添加到主 DEX 文件中:
3.build.gradle
android {
buildTypes {
release {
❶ multiDexKeepFile file('multidex-config.txt')
❷ multiDexKeepProguard('multidex-config.pro')
...
}
}
}
//multidex-config.txt
com/example/MyClass.class
com/example/MyOtherClass.class
//multidex-config.pro
-keep class com.example.MyClass
-keep class com.example.MyClassToo
-keep class com.example.** { *; } // All classes in the com.example package
但是在实际运行中,Android 4.x 的系统会在 APP 安装后第一次启动时,在MultiDex.install(this)
方法中进行 DEX 文件合并优化等耗时操作(主线程),往往会持续数十秒以上,从而导致 APP 第一次启动时长时间白屏,十分影响体验。
查阅相应的资料后大体有以下几种方案
设置主 Activity 的背景为透明色
这样当用户点击 APP 图标启动 APP 时,在主 Activity 启动之前看到的一直是桌面的样子而非白屏,但这只是一种障眼法,用户可能会以为系统卡顿,体验并不好。
在 Application 中检测到是第一次启动的话,新开一个进程并在其中进行
MultiDex.install(this)
这种方法在主进程启动时,检测到尚未进行 MultidexOpt,则阻塞当前进程,新开一个进程,在其中加载一个 Activity,并在后台进程运行
MultiDex.install(this)
,当 MultidexOpt 完成后再关闭当前进程,返回主进程继续正常开启 APP。由于主进程被阻塞的同时成为了后台进程,所以也不会触发 ANR,此外子进程中的过渡 Activity 也只用到了基本的类,所以基本不用担心会触发
java.lang.NoClassDefFoundError
,而且过渡 Activity 可以展示进度、提示等用户友好的页面,相对来说体验也好了很多。
但是这种方法从子进程返回主进程涉及到进程间通信,以及主进程的主 Activity 启动时生命周期会出现异常 (异常 (onCreate()
-> onStart()
-> onResume()
-> onPause()
->onResume()
),仍然不是很好的解决方法。
结合上述的分析后,可以看到这种问题的优化思路主要在于如何在避免java.lang.NoClassDefFoundError
的同时,在后台可靠的通过MultiDex.install(this)
执行 MultidexOpt 操作。
通过以上方案 1 和 2 的结合,可以有一个比较完美的解决方案:
- 方案 2 中在过渡 Activity 的后台线程进行 MultidexOpt 操作思路是正确的,但是不需要再单独开一个进程,我们完全可以将其当做主进程的第一个 Activity,等待 MultidexOpt 操作完成后再跳转到主 Activity 并 finish 掉本 Activity,这样主 Activity 的生命周期也不会受影响。
- 这种情况下在部分低端机上,过渡 Activity 到主 Activity 跳转时会出现短暂黑屏,我们可以在过渡页面将 Activity 切换动画设置为渐变效果,并将主 Activity 背景设置为透明,待主 Activity 完全加载好后再将背景切换为普通模式。
综上处理,我们的 Application 无需改动,甚至主 Activity 也可以不做改动,只需要添加一个过渡页面为启动 Activity,在其中后台进行 MultidexOpt,等 DEX 文件处理完毕后再加载主 Activity。对项目改动少并且逻辑较为简单。
注:
- MultiDexOpt 即执行
MultiDex.install(getApplication());
方法; - 需要注意过渡 Activity 尽量少的使用类,并且要确保过渡 Activity 可能会调用到的类加载到了主 dex 文件中。