一.关键词
1.冷启动
当点击应用图标启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用。这个启动方式就是冷启动。
因为冷启动每次都会创建初始化Application,所以Application的onCreate方法每次都会执行。也就是对应的APP级别的初始化方法都会执行,所以相对耗时多一点。
2.热启动
当点击应用图标启动应用时,后台已有该应用的进程(例如:按返回键退出APP,应用虽然退出,但是该应用的进程依然保留在后台,任务列表可查看),所以在已有进程的情况下,这种启动会从已有进程中启动应用。这个启动方式就是热启动。
因为热启动不会再创建初始化Application,只会重新创建初始化相关的Activity。所以耗时相对较少。
二.启动优化方案
APP的启动速度可能直接影响APP的使用情况,所以启动优化方案是必现的。下面讲解常用的几个方案。
1.启动黑屏问题
这么做的目的是启动时的黑白屏,给用户一种秒响应的感觉。其实不是真正减少启动时间,属于视觉错觉。
<1> 启动黑屏原因
Activity生命周期onCreate(),onStart(),onResume()方法。一次执行完这三个方法,APP才可和用户进行交互。如果这三个方法中执行了耗时操作,阻塞了主线程,很明显就会出现黑白屏问题。还有就是即使onResume()方法没有执行还是会出现黑白屏问题,原因就是加载界面需要短暂的时间。
<2> 解决方法
将启动页主题背景设置成闪屏页图片
自定义背景图样式
<style name="FristActivityTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@mipmap/XXX.png</item>
</style>
清单文件使用样式
<activity
android:name=".index.SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/FristActivityTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Activity可能要还原主题
package com.example.test;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
这种操作,其实是假象。主要给用户错觉。其实没有做到真正的启动优化。
2.异步或延时
项目中Application或者宿主Activity的onCreate中难免会初始化很多操作(比如 离线包初始化,高德地图初始化,APP数据库初始化等等)。如果一股脑的将这些初始化全部放到主线程中进行会降低启动速度。
所以可以把一些耗时的操作,比如 文件解压、读写、操作数据库等耗时 IO 操作到子线中执行。
还有一些初始化优先级比较低的(比如 WebView的一些初始化,因为只有进H5页面才会用到)。像这样的初始化可以延时加载,(即这种类似的操作可以放到某个用到的Activity中初始化,不必都集中到Application中初始化)这样的操作就不会影响启动耗时了。但是延时只能是延时一些优先级较低的。要避免延时初始化的内容,一进APP就使用,比如我遇到过的,锁屏推送埋点的问题。
子线程需要用到线程池
3.其他优化
比如A页面显示十五个ICON是接口获取的。进入B页面可以编辑,编辑之前要显示A页面的十五个ICON。那么B页面显示的编辑之前默认的十五个ICON就不用了接口请求。直接使用A页面请求回来的Json数据。同样B编辑成功后返回十五个新的ICON,回到A页面后,A页面也不必请求接口,直接解析B页面编辑成功后保存的Json数据即可。这样进入A页面—B页面编辑—A页面刷新,请求两个接口一遍即可。
二.启动耗时检测
下面讲解几种查看APP启动耗时的方法,来监控APP的启动速度。
1.代码打点
代码
package com.example.test;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initMethod();
}
public void initMethod() {
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.valueOf(i);
}
long endtime = System.currentTimeMillis();
long time = endtime - starttime;
Log.i("TAG", "测试方案耗时time----:" + time);
}
}
结果
I/TAG: 测试方案耗时time----:180
2.查看Logcat
谷歌在 Android4.4(API 19)上也提供了测量方法,在AndroidStudio Logcat中过滤关键字 “Displayed” ,来查看应用冷启动耗时日志。
结果
ActivityManager: Displayed com.example.test/.MainActivity: +238ms
ActivityManager: Displayed com.XXX/.webview.XXXActivity: +284ms
ActivityManager: Displayed com.XXX/.widget.XXX.view.XXXActivity: +286ms
3.命令行1
使用adb shell 获取应用的启动时间。
adb shell am start -W [包名]/[入口Activity全路径]
执行后会得到三个时间。
ThisTime:最后一个Activity启动耗时。
TotalTime:所有Activity启动耗时。
WaitTime:AMS启动Activity的总耗时。
输入
D:\Android\save\android-demo>adb shell am start -W com.XX/com.XX.main.activity.XXActivity
结果
<1> 进入首页
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.XX/.main.activity.XXXActivity }
Warning: Activity not started, its current task has been brought to the front
Status: ok
Activity: com.bankcomm/.main.activity.AAAActivity
ThisTime: 0
TotalTime: 0
WaitTime: 4
Complete
<2> 首页点击进入另一个Activity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.XXX/.main.activity.XXXActivity }
Warning: Activity not started, its current task has been brought to the front
Status: ok
Activity: com.XXX/.webview.BBBActivity
ThisTime: 0
TotalTime: 0
WaitTime: 8
Complete
4.命令行2
adb shell 然后输入 logcat -b events|grep am_activity_launch_time
输入
D:\Android\save\android-demo>adb shell
NX606J:/ $ logcat -b events|grep am_activity_launch_time
结果
I am_activity_launch_time: [0,124154950,com.XXX/.main.activity.AAAActivity,488,488]
I am_activity_launch_time: [0,161791135,com.XXX/.webview.BBBActivity,284,284]
I am_activity_launch_time: [0,151415468,com.XXX/.widget.XXX.view.CCCActivity,286,286]