应用启动分类
冷启动
用户点击屏幕上的应用图标,经过展示启动窗口
、创建进程
、展示应用
几个过程
热启动
用户进程已经创建,如果响应了低内存事件,例如在 onTrimMemory
中清除资源,则需要重新初始化
如何检测应用启动时长
- 通过
adb shell am start -S -W $packageName/.MainActivity
启动检测 - 通过显示调用
reportFullDrawn
根据业务需求,通知系统完成加载 - 通过
android-sdk/platform-tools/systrace
下systrace.py
通过Python systrace.py --time=10 -o launch.html sched gfx view wm
查看每个函数的耗时 - 通过
Android Studio Profiler
启动应用查看
为什么会出现白屏
在应用启动过程中 ActivityStack
的 startActivityLocked
方法中会判断当前应用的启动模式,若为冷启动,则调用 ActivityRecord
中 windowContainerController
的 showStartingWindow
方法,添加启动白屏页
通过 WindowMangerService
调用 mPolicy
的 addSplashScreen
方法,创建了一个 PhoneWindow
添加到窗口上
关键代码
if (theme != context.getThemeResId() || labelRes != 0) {
try {
context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
view = win.getDecorView();
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
wm.addView(view, params);
如何优化
启动优化步骤分类
- 白屏的视觉优化
MultiDex
优化- 逻辑代码优化
- 首页布局优化
白屏的优化
对于白屏启动页面的优化,根据上述的代码分析,可以通过设置主题,为 windowBackground
添加与启动页一致的图片,视觉上可以骗过用户
MultiDex 优化
着应用的不断迭代,内部的方法数会不断增加,最终超过方法数上限。而官方也退出了 MultiDex
的方案来解决,也就是意味着分包。在低版本手机 DVM 上,我们需要手动调用 MultiDex.install
加载主 Dex
以外的文件,可能造成 ANR
。
低版本启动的优化
面对这个问题,我们可以采用 Facebook 提出的方案。创建一个新的进程在 Application
的 attachBaseContext
中调用 MultiDex.install
,主进程可以通过多种手段阻塞等到加载结束之后再进入应用
class App : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
if (isAsyncLaunchProcess().not()) {
val thread = DexInstallDeamonThread(base)
thread.start()
synchronized(lock) {
try {
lock.wait()
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
thread.exit()
}
}
fun isAsyncLaunchProcess(): Boolean {
val processName = SystemUtils.getCurrentProcessName(this)
return processName != null && processName.contains(":install")
}
private class DexInstallDeamonThread(private val base: Context) :
Thread() {
private var handler: Handler? = null
private var looper: Looper? = null
@SuppressLint("HandlerLeak")
override fun run() {
Looper.prepare()
looper = Looper.myLooper()
handler = object : Handler() {
override fun handleMessage(msg: Message) {
synchronized(lock) { lock.notify() }
}
}
val messenger = Messenger(handler)
val intent = Intent(base, MultiInstallActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra("MESSENGER", messenger)
base.startActivity(intent)
Looper.loop()
}
fun exit() {
if (looper != null) looper!!.quit()
}
}
}
class MultiInstallActivity : AppCompatActivity() {
private var messenger: Messenger? = null
public override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
setContentView(R.layout.activity_install)
val from = intent
messenger = from.getParcelableExtra("MESSENGER")
thread {
try {
Log.d(TAG, "start install")
MultiDex.install(application)
Thread.sleep(5000L)
Log.d(TAG, "finish install")
messenger?.send(Message.obtain())
} catch (e: Exception) {
e.printStackTrace()
}
runOnUiThread {
finish()
exitProcess(0)
}
}
}
override fun onBackPressed() {
//无法退出
}
companion object {
private const val TAG = "MultiInstallActivity"
}
}
<activity
android:name="com.weex.multidexinstall.MultiInstallActivity"
android:launchMode="singleTask"
android:process=":install" />
逻辑代码优化
在应用启动过程中,我们一般会在 Application
的 onCreate
中加载 SDK
以及读取 SharedPreference
中的值等耗时操作
- 对于
SharedPreference
可以替换为MMKV
首页布局优化
对于布局优化,比较常见的一些技术如
ViewStub
merge
标签- 使用
ConstraintLayout
替换原来的布局标签
本文转自 https://juejin.cn/post/7054214019749511205,如有侵权,请联系删除。