1、冷启动:当启动应用时,后台没有该应用的进程,系统要重新创建一个新的进程分配给该应用,这种启动方式就是冷启动。冷启动首先会为应用创建一个新进程,然后先后创建和初始化Application与启动Activity,最后将界面显示出来。
2、热启动:当启动应用时,后台已经存在该应用的进程(比如:按Home键、Back键的时候,应用虽然退出了前台,但后台依然保存着应用的进程),这种情况下,会直接从已有的进程中启动应用,这种叫做热启动。热启动会直接将应用从后台拉起,不走App进程、Application、Activity的创建和初始化这几步,而是直接将应用显示在前台。
从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,放置一张背景图,如下
在windowBackground中放置一个背景图,当然我还设置了其他属性使图片全屏显示,然后将样式设置给启动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三个数据,那么这三个时间分别代表什么呢?
1.ThisTime:所有启动Activity中最后那个Activity的启动耗时
2.TotalTime:所有Activity的启动耗时,因为例子中,整个启动过程中就启动SplashActivity这个Activity,所以ThisTime和TotalTime相等
3.WaitTime:从AMS启动App到Activity的总时间,在启动一个Activity之前会先调用当前activity的onPause,然后通过AMS去启动后面的步骤。所以说这个WaitTime就包括了当前Activity的onPause时间。
从前面我们知道,App启动时间的优化我们重点关注的是Application和第一个显示Activity的初始化操作,一般来说项目都会在Application的onCreate方法中做一些全局的初始化,所以下一步去看看Application中做了一些什么初始化操作。
这是我的Application中的初始化操作
//EventBus初始化
EventBus.getDefault().register(this);
// 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
SDKInitializer.initialize(this);
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
//初始化友盟
initUm();
//初始化路由相关
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();
//初始化路由相关
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的初始化,友盟的初始化,百度地图的初始化。
一些常用的优化思路:
因为首页activity要用到组件ARouter,所以我考虑把它的初始化延时到SplashActivity中的使用前,经测试,百度地图不能在子线程初始化,所以考虑延时,然后友盟初始化放在子线程中
private void initOnWorkThread() {
new Thread(new Runnable() {
@Override
public void run() {
//将线程设置为后台,避免和主线程争抢资源
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
initUm();
initCrashReport();
}
}).start();
}
//延迟在SplashActivity中初始化,界面完成以后才执行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启动时间优化的总结和实践!