启动速度优化总结(上)

1、背景

最近用户反馈应用启动慢,经测试人员确实存在首次启动慢的问题。为了优化应用启动时间,本文主要是记录排查启动慢问题。

2、启动方式

应用启动分为冷启动、温启动、热启动,本文主要是针对冷启动进行优化。冷启动有两种优化方案,一种是通过使用启动页提高用户体验,一种是优化启动效率,本文主介绍如何进行启动效率的优化。

3、应用启动过程

简单的解释一下应用启动过程:
点击Launcher,启动程序,通过Binder跨进程调用ActivityManagerService
ActivityManagerService通知zygote进程孵化出应用进程,分配内存空间等
执行应用ActivityThread的main()方法,开启Loop。
应用程序通知ActivityManagerService它已经启动,ActivityManagerService保存一个该应用的代理对象,ActivityManagerService通过它可以控制应用进程
ActivityManagerService通知应用进程创建入口的Activity实例,执行它的生命周期
启动过程中Application和入口Activity的生命周期方法按如下顺序调用:
Application 构造方法
attachBaseContext()
onCreate()
入口Activity的对象构造
setTheme() 设置主题等信息
入口Activity的onCreate()
入口Activity的onStart()
入口Activity的onResume()
入口Activity的onAttachToWindow()
入口Activity的onWindowFocusChanged()

4、启动时间计算方式

优化的效果需要具体的数据来支撑,计算应用的启动时间有以下几种方式,我们一一介绍:

  • 打印log
    我们知道了应用启动的流程,很明显我们在Application的构造方法中可以打印记录一个logTime,这是用户点击Launcher触发的第一个点。
    那我们在什么位置打印记录结束的时间呢?我们知道在Activity生命周期onResume回调的时候,应用代表可见了,可这种意义上其实是不严谨。

因为在这个时间点,实际只完成了主题信息以及view树建立等。而view绘制的真正过程measure layout draw过程并没有,所以不是真正意义上的“可见”。

那有没有更好的时间点呢?有的,那就是onWindowFocusChanged(),我们看看onWindowFocusChanged()的注释:

/**
    * Called when the current {@link Window} of the activity gains or loses
    * focus.  This is the best indicator of whether this activity is visible
    * to the user.  The default implementation clears the key tracking
    * state, so should always be called.
    *....
    */
  public void onWindowFocusChanged(boolean hasFocus) {
  }

通过注释我们很清楚的知道,对用户可见的时间点就是这里。这样我们在这里打印记录一个logTime,那我们就能计算出应用的启动时间了。

05-11 17:06:33.937 : GoKeyboardApplication: time: 1494493593937
05-11 17:06:34.703 : MainActivity onCreate: time: 1494493594703
05-11 17:06:35.236 : MainActivity onWindowFocusChanged: time: 1494493595236
  • 使用ADB命令进行统计
其实还有一个方法,那就是使用ADB命令:
// adb shell am start -W 包名/入口类全路径名
adb shell am start -W [PackageName]/[PackageName.MainActivity]

Status: ok
Activity: 
ThisTime: 1426
TotalTime: 1426
Complete

这里面涉及到三个时间,ThisTime、TotalTime 和 WaitTime。
WaitTime 是 startActivityAndWait 这个方法的调用耗时;
ThisTime 是指调用过程中最后一个 Activity 启动时间到这个 Activity 的 startActivityAndWait 调用结束;
TotalTime 是指调用过程中第一个 Activity 的启动时间到最后一个 Activity 的 startActivityAndWait 结束。
如果过程中只有一个 Activity ,则 TotalTime 等于 ThisTime。

其实两种方式在实际使用过程中并没有多大的差异,根据数据对比,ADB方式数据会更准确一点,并且整体上使用ADB命令会简单。
但是使用ADB命令在部分启动过慢的应用上会经常“Timeout”,所有建议两种方式结合使用。

4、Traceview工具

知道了应用启动时间后,开始学习分析工具Traceview的使用。Traceview是一个性能分析工具, 主要是分析当前线程情况, 各个方法执行时间等.
通过Traceview可以很方便的找出“执行时间长”的方法,我们可以“对症下药”。接下来看看如何使用:

// 在Applicatino的构造函数中开启
Debug.startMethodTracing("TestApp");
// 在MainActivity的onWindowFocusChanged();中关闭
Debug.stopMethodTracing();

启动应用后,在sdcard根目录就会生成一个TestApp.trace文件(当然你的APP需要android.permission.WRITE_EXTERNAL_STORAGE权限)。
我们可以通过ADB命令导出文件

adb pull sdcard/TestApp.trace /home/

拿到TestApp.trace文件后,通过DDMS打开文件:
这里写图片描述

我们可以看到分为上下两部分,上面是图形化显示的线程对应的时间轴,下部分是具体调用方法。
我们直观的可以发现mainThread的时间轴很长,说明大部分任务在mainThread中进行。
有很多tab,我们最关心的是以下两个
Real Time/Call(实际发生时间)
Calls+RecurCalls/Total(发生次数)
通过Real Time/Call 降序排列可以看到程序中代码执行耗时情况;这样我们就能分析是哪些方法导致启动慢了。

5、总结

上面简介了启动时间的计算方法,已经分析工具的使用, 下一篇会结合输入法项目进行启动项分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值