一、启动定义
启动:从手指点击app图标,到第一个页面加载完成.
二、启动类型
在安卓中应用的启动方式分为以下几种:
1.冷启动
冷启动:当启动应用时,后台没有该应用的进程,系统会创建一个新的进程分配给该应用。
冷启动会先创建和初始化 Application 类,再创建和初始化 MainActivity 类,最后显示在界面上。
2 .热启动
热启动:当启动应用时,后台已有该应用的进程(如:按back键、home键)。
热启动只会创建和初始化 MainActivity 类,不会创建和初始化 Application,因为Application 只会初始化一次。
3 .首次启动
首次启动:安装应用后第一次启动的过程。
首次启动也是冷启动,比起冷启动,还会做一些系统初始化工作,如缓存目录创建、数据库创建,所以首次启动花费的时间最长。首次启动的速度非常重要,毕竟影响用户对 App 的第一映像。
三、启动流程
1.创建流程
- 点击应用图标后,进入Launcher的onClick(), 然后调用 startActivity (),然后调用 Instrumentation 的 execStartActivity()。
- Instrumentation 调用 ActivityManagerProxy (ActivityManagerService 在应用进程的一个代理对象) 的 startActivity ()。
- ActivityManagerProxy 跨进程调用 ActivityManagerService (运行在 system_server 进程)的 startActivity ()。
- ActivityManagerService 最后调用 zygoteSendArgsAndGetResult(), 通过 socket 发送给 zygote 进程,zygote 进程会孵化出新的应用进程。
- zygote 执行 ActivityThread 的 main()。在该方法里会先准备好 Looper 和消息队列,然后调用 attach 方法将应用进程绑定到 ActivityManagerService,然后进入 loop 循环,不断地读取消息队列里的消息,并分发消息。
- ActivityManagerService 保存应用进程的一个代理对象,然后 ActivityManagerService 通过代理对象通知应用进程创建入口 Activity 的实例,并执行它的生命周期函数。
2.生命周期
创建应用进程之前的流程由系统决定,没办法优化。能够优化的是Application和MainActivity 的创建和初始化。
下面是 MainActivity 的启动流程:
-> Application 构造函数
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity.setTheme()
-> Activity 构造函数 -> Activity.onCreate()
-> Activity.onAttachedToWindow
-> Activity.onStart -> Activity.onResume
-> Activity.onWindowFocusChanged
四、启动时间
1.Activity启动时间统计
通过命令行方式统计启动时间:
adb shell am start -W appplicationId/packageName.activityName
输出的结果类似于:
$ adb shell am start -W com.speed.test/com.speed.test.HomeActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.speed.test/.HomeActivity }
Status: ok Activity: com.speed.test/.HomeActivity ThisTime: 496 TotalTime: 496 WaitTime: 503
Complete
-
ThisTime
最后一个Activity的启动耗时; -
TotalTime
所有Activity(指定的Activity可能启动其他Activity)的启动耗时;。 -
WaitTime
调用startActivity() 到最后一个Activity启动完成的时间,即当前Activity的onPause()时间+TotalTime
;
2.精确时间统计
通过trace文件分析每个方法的花费时间.
1.生成trace文件
- 使用代码生成
先通过Debug类生成trace文件,注意加读写权限。
Debug.startMethodTracing("shixintrace");
- ...
Debug.stopMethodTracing();
注意:在Applicaiton中开始trace,然后再MainActivity中结束trace,不会生成trace文件。
trace文件生成后存放在SD卡中,可能直接存放/sdcard,也可能存在外置专有目录中。
先通过以下命令找到具体路径:
adb shell
- cd /sdcard
find -name *.trace
- 可能得到如下结果
./Android/data/gsw.test1/files/shixintrace.trace
然后退出shell状态,通过adb命令取出文件
adb pull sdcard/Android/data/gsw.test1/files/shixintrace.trace
- 使用 Android Studio生成
生成 trace 文件:Android Monitor -CPU监控-闹钟。
2.分析trace文件
如果使用代码生成trace文件,则需要用Android Studio打开.
分析trace文件:
- 先分析线程的执行时间,对时间较长的线程,再分析每个方法的执行时间和调用次数。
- 对于执行时间长的方法,需要分析每一步的执行时间,知道找到最深层的长耗时方法。
- 对于调用频繁的方法,需要逐步逐层分析调用和依赖。
五、优化方案
1.减少启动耗时
- 在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。
- 对于sp(首选项)的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
- 对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。
2.黑白屏优化
- 设置特定的背景
通过设置theme的android:windowBackground属性实现。
如果设置的背景在应用启动完成后不再需要,可以再通过setTheme()设置背景。 - 不显示背景
通过设置theme的android:windowIsTranslucent属性实现:true代表透明;false代表非透明。