一:App启动方式
1、冷启动:当启动应用时,后台没有该应用的进程,系统要重新创建一个新的进程分配给该应用,这种启动方式就是冷启动。冷启动首先会为应用创建一个新进程,然后创建并初始化Application和Activity,最后将界面显示出来
2、热启动:当启动应用时,后台已经存在该应用的进程(比如:按Home键、Back键的时候,应用虽然退出了前台,但后台依然保存着应用的进程),这种情况下,会直接从已有的进程中启动应用,这种叫做热启动。热启动会直接将应用从后台拉起,不走App进程、Application、Activity的创建和初始化这几步,而是直接将应用显示在前台
二:App启动流程
从App启动方式来看,冷启动最缓慢,App本身的工作要从头开始,下面是App冷启动的流程:
1.加载启动App
2.启动后立即显示出一个空白的Window,就是我们常见的白屏或黑屏
3.创建APP进程
4.创建App主线程
5.创建Application对象
6.创建Activity对象
7.View绘制,第一次显示屏幕
上面的步骤1~4是启动App固有的系统流程,不是我们能控制的,所以不在优化的考虑范围内,可优化的空间是Application及Activity的创建、初始化
Google给我们的启动加速方向是:
1.替换提前展示的空白Window界面,给用户一个快速打开APP的感觉
2.避免在启动时做密集沉重的初始化工作
3.定位问题:避免I/O操作、反序列化、网络操作、布局嵌套等
接下来就基于这个几方向去讲解一下App启动优化的方法
三:启动之主题替换
通常打开一个App的时候都是立即就展示出一个欢迎界面之类的,而不是白屏或者黑屏,其实这是对启动界面的主题进行了替换而已
在我们的styles文件中自定义一个样式Launcher,放置一张背景图,如下
<style name="CustomActionBarTheme.Launcher">
<item name="android:windowBackground">@drawable/splash_bg</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
在windowBackground中放置一个背景图,设置其他属性使图片全屏显示,然后将样式设置给启动Activity
<activity android:name=".activity.SplashActivity"
android:theme="@style/CustomActionBarTheme.Launcher"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
这样启动时候就可以立即展示你所设置的背景图了,如果想要在启动Activity中显示回原来的主题,可以在Activity中这样设置
@Override
protected void onCreate(Bundle savedInstanceState) {
//替换为原来的主题,在onCreate之前调用
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
四:启动时间计算
在cmd中输入adb shell am start -W [包名]/[全类名],即通过adb启动我们的Activity,控制台会输出启动时间
主要关注ThisTime、TotalTime和WaitTime三个数据,那么这三个时间分别代表什么呢?
- ThisTime:所有被启动的Activity中最后那个Activity的启动耗时
- TotalTime:所有Activity的启动耗时,包括创建进程+Application初始化+Activity初始化到界面显示,一般开发者只需要关心这个值,这才是应用的真正启动耗时;例子中一共就启动了一个Activity,所以ThisTime和TotalTime相等
- WaitTime:就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间
App启动时间的优化重点关注的是Application和第一个显示Activity,一般来说项目都会在Application的onCreate方法中做一些全局的初始化工作,所以下一步去看看我们的Application中做了什么
五:App启动的优化
这是一个Application中的初始化操作
//EventBus初始化
EventBus.getDefault().register(this);
// 百度地图的初始化,在使用SDK各组间之前初始化context信息,传入ApplicationContext
SDKInitializer.initialize(this);
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
//初始化友盟
initUm();
//初始化路由Arouter
initRouter(this);
//初始化日志收集
initCrashReport();
//初始化http
initRxHttp();
现在我想统计上面这些方法的执行耗时,在代码之间插入Debug.startMethodTracing(filename)和Debug.stopMethodTracing()就可以追踪所包含代码的执行耗时,这些数据会写入一个.trace文件中,只要导出这个文件就可以看到详细信息了
//代码追踪开始,参数是trace文件保存在手机的全路径名
Debug.startMethodTracing(this.getExternalFilesDir(Environment.MEDIA_MOUNTED).getPath() + "01");
//EventBus初始化
EventBus.getDefault().register(this);
// 百度地图的初始化,在使用SDK各组间之前初始化context信息,传入ApplicationContext
SDKInitializer.initialize(this);
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
//初始化友盟
initUm();
//初始化路由ARouter
initRouter(this);
//初始化日志收集
initCrashReport();
//初始化http
initRxHttp();
//代码追踪结束
Debug.stopMethodTracing();
执行以上代码,然后通过将在cmd中输入adb pull [trace文件保存在手机的全路径名] 导出trace文件到电脑中,再通过Android Studio打开文件
从图中可以看到代码中每个方法的执行时间和占用时间的百分比,initRouter时间占比55.6%、initUm占比20.1%、SDKInitializer.initialize占比19.5%,主要是这个三个初始化占用了大多数时间,它们分别是路由框架ARouter,友盟和百度地图的初始化
一些常用的优化思路:
- 考虑异步初始化第三方组件,不阻塞主线程
- 延迟第三方的初始化时间,因为异步初始化有可能遇到初始化还没完成主线程已经用到的情况,这种情况可以考虑延时到第三方组件使用之前进行初始化
- 数据库、IO操作、网络请求尽量不要在Application中执行,能异步初始化的就尽量异步,如果不能异步初始化就尽量延时,不要在Application中创建线程池
- 在首页Activity中,布局尽量减少嵌套,在onCreate、onStart、onResume方法中尽量避免耗时操作
因为首页activity要用到组件ARouter,所以我考虑把它的初始化延时到SplashActivity中的使用前,经测试,百度地图不能在子线程初始化,所以考虑延时,然后友盟初的始化放在子线程
//在Application中创建工作线程进行相关初始化
private void initOnWorkThread() {
new Thread(new Runnable() {
@Override
public void run() {
//将线程设置为后台,避免和主线程争抢资源
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
initUm();
initCrashReport();
}
}).start();
}
//将下面初始化延迟到首次显示Activity的onCreate方法中进行,界面完成以后才执行run方法
//要让界面先显示出来,初始化完成以后再进行登录等其他操作
findViewById(R.id.splash_view).post(new Runnable() {
@Override
public void run() {
//初始化ARouter
ARouter.init(ComApplication.getInstance());
// 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
SDKInitializer.initialize(ComApplication.getInstance());
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
//登录等其他操作
........
}
});
来看一下优化以后的启动时间
六:优化前后对比
优化前时间 | 优化后时间 | 时间相差 | 提升百分比 |
1560 | 1009 | 551 | 35.3% |
优化启动速度整整提升了35.3%,效果还是比较满意的,上面就是我对App启动时间优化的总结和实践