为什么要做App的启动优化?
网页端存在的一个定律叫8秒定律:即指用户访问一个网站时,如果等待打开的时间超过8秒,超过70%的用户将会放弃等待。
同样的,移动端也有一个8秒定律:如果一个App的启动时间超过8秒或有明显的卡顿,80%的用户将会退出应用并对程序员进行口吐芬芳。当然这是我瞎编的,但却不代表是不存在的。最起码肯定会影响App在市场上的评分,进而让更多的用户在对比过程中选择竞品。
知道了启动优化的重要性,那么接下来我们就来分析下如何优化App的启动,本文内容主要分为以下三部分:
分析优化分析优化方向
App应用主要有三种启动状态:冷启动、热启动和温启动。
1、 冷启动:耗时最长,也是主要的优化点;(恋爱前的女人)
冷启动前,系统主要做了三件事:
- 加载并启动应用。
- 在启动后立即显示应用的空白启动窗口。
- 创建应用进程。
创建应用进程后:
- 创建应用对象。
- 启动主线程。
- 创建主 Activity。
- 扩充视图。
- 布局屏幕。
- 执行初始绘制
2、热启动:耗时最短,将activity从后台带到前台;(热恋中的女人)
3、温启动:耗时较长,重走了Actiivty的生命周期。(结婚后的女人)
从应用的启动状态中,我们可以分析得出,剥除系统本身的任务动作外(这部分我们是无法进行操作修改的),其实我们的启动优化方向主要就是:Application和Activity的生命周期、主视图的布局优化
相关数据测量
优化App的启动速度前,我们得先获取App的一些启动数据,根据这些数据才能准确找到优化的点,才能对优化后的操作做一个准确的评估。(下面的相关代码我将会拿之前的一个旧项目来做演示,一是更贴近实际开发情况,比demo更加直观;二是顺手给优化了,何乐而不为呢?)
1、获取启动时间
adb命令法:adb shell am start -S -W packagename/activity(含包名)
- ThisTime:最后一个 Activity 启动时间;
- TotalTime:所有 Activity 启动耗时(这里只启动了一个 MainActivity);
- WaitTime:AMS 启动 Activity 的总耗时;
adb 命令虽然简单好用,但还是有不少缺点的:
- 只能线下使用,而在实际开发过程中,用户的启动时间才是最好的参考指标;
- 非精确的时间,这里只是显示了 Activity 启动完毕的时间,但对于用户的直观体验来说,只有首页的数据展示出来,才算是真正的启动完成。手动打点法
我们先定义一个LaunchTimer类,用来记录启动时间:
public class LaunchTimer {
private static long mTime;
//开启时间
public static void startTime() {
mTime = System.currentTimeMillis();
}
//结束时间
public static void endTime() {
LoggerManager.d("启动时间:" + (System.currentTimeMillis() - mTime));
}
}
在Application类的attachBaseContext()方法中打入开始启动时间点:
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
LaunchTimer.startTime();
}
在我们的首页第一条数据展示成功后打入结束时间点:(ps:网上很多文章都在 onwindowfocuschanged()方法中打入结束时间,其实这个方法只是首帧时间,并不代表我们的页面数据等全部展示出来了。我们做优化,还是得以用户的实际体验来作为参考价值,不能仅仅KPI化)
//是否已经记录启动时间
private boolean mIsRecord = false;
@Override
protected void convert(BaseViewHolder helper, final HomeListBean.DataBean item) {
if (helper.getPosition() == 1 && !mIsRecord) {
mIsRecord = true;
final View contentView = helper.getView(R.id.home_item_rl);
//监听第一条数据的绘制完成时间
contentView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
contentView.getViewTreeObserver().removeOnPreDrawListener(this);
LaunchTimer.endTime(); return true;
}
});
}
}
运行我们的代码,可以看到启动时间是3111毫秒,正常来说,是会比用adb命令打出的时间要长点。
手动打点的好处:
- 可以线上使用,统计真实用户的启动时间
- 时间准确,结合用户真实体验,参考价值更高
2、其他优化分析工具
我经常使用的启动优化工具主要有Traceview(官方文档)和systrace(官方文档),Traceview虽然比较全面,但性能消耗太大,这里不做过多介绍,有兴趣的朋友可以自行查看官方文档,这里主要介绍 systrace这个工具(使用前需得先安装python)。
先打点:
public void onCreate() {
super.onCreate();
//使用兼容的TraceCompat打入开始点
TraceCompat.beginSection("AppBegin");
if (instance == null) {
instance = this;
}
if (IS_DEBUG_ABLE) {
initLogger();
}
initBugly();
Tiny.getInstance().init(this);
//初始化tiny图片压缩工具
initJPush();
initSkin();
RichText.initCacheDir(this);
//设置缓存
initFragmentation();
MMKV.initialize(this);
TraceCompat.endSection();
//使用兼容的TraceCompat记录结束点
TraceCompat.endSection();
}
安装App,使用systrace命令:python systrace.py -b 32768 -t 10 -a packagename -o outputfile.html sched gfx view wm am app (命令执行过程中点击启动App)
运行操作后,打开我们的html文件,可以看到我们app的启动相关数据:
运行操作后,打开我们的html文件,可以看到我们app的启动相关数据:
图中红圈部分是我们需要注意的地方,AppBegin就是我们打点的代表区间,可以看到这段区间时间是 732.127毫秒。最下面有两个数值,一个是WallDuration,这个就是我们代码的执行时间,另一个 CPUDuration是我们的CPU执行时间。
如需完整版 Android性能优化学习笔记 请点击免费获取
优化技巧
终于讲到我们的优化技巧了,具体优化我们可以分为以下几种方式:
- 闪屏优化
- 业务优化
- 线程优化
- UI优化