安卓(Android)开发之统计App启动时间

前言

作为 Android 开发者,想必多多少少要接触启动速度优化相关的事情,当用户越来越多,产品的功能也随着迭代越来越多,App 逐渐变得臃肿是一件很常见的现象,甚至可以说是不可避免的现象,随之而来的工作就是优化 App 性能,其中最主要的一项就是启动速度优化。但本文的主角并不是启动速度优化,而是启动时间统计。

一、启动类型

工欲善其事,必先利其器。想要优化 App 的启动速度,必须有准确衡量启动时间的方法,否则优化完之后效果怎样,自己都不知道,说出去别人也不信服不是。在做 App 启动时间统计之前,当然必须弄明白有哪些启动类型,每种启动类型的特点。通常来说,在安卓中应用的启动方式分为以下几种:

1、冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化 Application 类,再创建和初始化 MainActivity 类,最后显示在界面上。

2、热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。热启动因为会从已有的进程中来启动,所以热启动就不会走 Application 这步了,而是直接走 MainActivity,所以热启动的过程不必创建和初始化 Application,因为一个应用从新进程的创建到进程的销毁,Application 只会初始化一次。

3、首次启动:首次启动严格来说也是冷启动,之所以把首次启动单独列出来,一般来说,首次启动时间会比非首次启动要久,首次启动会做一些系统初始化工作,如缓存目录的生产,数据库的建立,SharedPreference的初始化,如果存在多 dex 和插件的情况下,首次启动会有一些特殊需要处理的逻辑,而且对启动速度有很大的影响,所以首次启动的速度非常重要,毕竟影响用户对 App 的第一映像。

二、本地启动时间的统计方式

如果是本地调试的话,统计启动时间还是很简单的,通过命令行方式即可:

adb shell am start -w packagename/activity

输出的结果类似于:

$ adb shell am start -W com.speed.test/com.speed.test.HomeActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.speed.test/.HomeActivity }
Status: ok
Activity: com.speed.test/.HomeActivity
ThisTime: 496
TotalTime: 496
WaitTime: 503
Complete

WaitTime 返回从 startActivity 到应用第一帧完全显示这段时间. 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;

ThisTime 表示一连串启动 Activity 的最后一个 Activity 的启动耗时;

TotalTime 表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用Activity pause的耗时。

开发者一般只要关心 TotalTime 即可,这个时间才是自己应用真正启动的耗时。

三、线上启动时间的统计方式

当 App 发到线上之后,想要统计 App 在用户手机上的启动速度,就不能通过命令行的方式进行统计了,基本上都是通过打 Log 的方式将启动时间发送上来。那么在什么位置加启动时间统计的 Log 就尤为重要,Log 添加的位置直接决定启动时间统计的是否准确,同样也会影响启动速度优化效果的判断。要想找到合适准确的位置记录启动时间的 Log,就需要了解应用的启动流程,和各个生命周期函数的调用顺序。下面来分析下到底在什么位置打 Log 记录启动时间比较合适。

应用的主要启动流程

关于 App 启动流程的文章很多,文章底部有一些启动流程相关的参考文章,这里只列出大致流程如下:

1、通过 Launcher 启动应用时,点击应用图标后,Launcher 调用 startActivity 启动应用。

2、Launcher Activity 最终调用 Instrumentation execStartActivity 来启动应用。

3、Instrumentation 调用 ActivityManagerProxy (ActivityManagerService 在应用进程的一个代理对象) 对象的 startActivity 方法启动 Activity

4、到目前为止所有过程都在 Launcher 进程里面执行,接下来 ActivityManagerProxy 对象跨进程调用 ActivityManagerService (运行在 system_server 进程)的 startActivity 方法启动应用。

5、ActivityManagerService startActivity 方法经过一系列调用,最后调用 zygoteSendArgsAndGetResult 通过 socket 发送给 zygote 进程,zygote 进程会孵化出新的应用进程。

6、zygote 进程孵化出新的应用进程后,会执行 ActivityThread 类的 main 方法。在该方法里会先准备好 Looper 和消息队列,然后调用 attach 方法将应用进程绑定到 ActivityManagerService,然后进入 loop 循环,不断地读取消息队列里的消息,并分发消息。

7、ActivityManagerService 保存应用进程的一个代理对象,然后 ActivityManagerService 通过代理对象通知应用进程创建入口 Activity 的实例,并执行它的生命周期函数。

总结过程就是:用户在 Launcher 程序里点击应用图标时,会通知 ActivityManagerService 启动应用的入口 ActivityActivityManagerService 发现这个应用还未启动,则会通知 Zygote 进程孵化出应用进程,然后在这个应用进程里执行 ActivityThread main 方法。应用进程接下来通知 ActivityManagerService 应用进程已启动,ActivityManagerService 保存应用进程的一个代理对象,这样 ActivityManagerService 可以通过这个代理对象控制应用进程,然后 ActivityManagerService 通知应用进程创建入口 Activity 的实例,并执行它的生命周期函数。

生命周期函数执行流程

上面的启动流程是 Android 提供的机制,作为开发者我们需要清楚或者至少了解其中的过程和原理,但我们并不能在这过程中做什么文章,我们能做的恰恰是从上述过程中最后一步开始,即 ActivityManagerService 通过代理对象通知应用进程创建入口 Activity 的实例,并执行它的生命周期函数开始,我们的启动时间统计以及启动速度优化也是从这里开始。下面是 Main Activity 的启动流程:

-> Application 构造函数
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity 构造函数
-> Activity.setTheme()
-> Activity.onCreate()
-> Activity.onStart
-> Activity.onResume
-> Activity.onAttachedToWindow
-> Activity.onWindowFocusChanged

如果打 Log 记录 App 的启动时间,那么至少要记录两个点,一个起始时间点,一个结束时间点。

起始时间点

起始时间点比较容易记录:如果记录冷启动启动时间一般可以在 Application.attachBaseContext() 开始的位置记录起始时间点,因为在这之前 Context 还没有初始化,一般也干不了什么事情,当然这个是要视具体情况来定,其实只要保证在 App 的具体业务逻辑开始执行之前记录起始时间点即可。如果记录热启动启动时间点可以在 Activity.onRestart() 中记录起始时间点。

结束时间点

结束时间点理论上要选在 App 显示出第一屏界面的时候,但是在什么位置 App 显示出第一屏界面呢?网上很多文章说在 Activity onResume 方法执行完成之后,Activity 就对用户可见了,实际上并不是,一个 Activity 走完onCreate onStart onResume 这几个生命周期之后,只是完成了应用自身的一些配置,比如 Activity 主题设置 window 属性的设置 View 树的建立,但是其实后面还需要各个 View 执行 measure layout draw等。所以在 OnResume 中记录结束时间点的 Log 并不准确,大家可以注意一下上面流程中最后一个函数 Activity.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.
...
*/

通过注释我们可以看到,这个函数是判断 activity 是否可见的最佳位置,所以我们可以在 Activity.onWindowFocusChanged 记录应用启动的结束时间点,不过需要注意的是该函数,在 Activity 焦点发生变化时就会触发,所以要做好判断,去掉不需要的情况。

总结

以上就是关于安卓(Android)开发之统计App启动时间的全部内容,本文的内容小编觉得还是很重要的,还是那句话:工欲善其事,必先利其器,准备工作做的充分,做事自然有理有据。希望本文的内容对大家有所帮助。

(0)

相关推荐

  • Android app启动时黑屏或者白屏的原因及解决办法

    1.产生原因 其实显示黑屏或者白屏实属正常,这是因为还没加载到布局文件,就已经显示了window窗口背景,黑屏白屏就是window窗口背景. 示例: 2.解决办法 通过设置设置Style (1)设置背景图Theme 通过设置一张背景图. 当程序启动时,首先显示这张背景图,避免出现黑屏 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name=&qu

  • Android 6.0指纹识别App开发案例

    在android 6.0中google终于给android系统加上了指纹识别的支持,这个功能在iPhone上早就已经实现了,并且在很多厂商的定制的ROM中也都自己内部实现这个功能了,这个功能来的有点晚啊.在google全新发布的nexus设备:nexus 5x和nexus 6p中都携带了一颗指纹识别芯片在设备的背面,如下图(图片来自网络): 笔者手中的设备就是图上的那台黑色的nexus 5x,话说这台机器很是好看呢!手感超棒! 废话不多说,下面我出一个指纹识别的demo app,并且详细说明怎么

  • Android实现APP欢迎页面简单制作思路

    现在的APP一般都会在应用启动时有一个欢迎界面,这种欢迎界面一般有两种情况,一种用来在应用启动时在后台检测网络状态,亦或者是加载网络数据等一些耗时操作,比如QQ.微信.还有一种是在启动页方式自己的品牌标志,一般为Logo,例如淘宝.今天我将通过简单地代码来实现这两种效果. 1.在后台加载网络数据,运行一些耗时的任务,代码如下: WelcomeActivity.java文件代码 public class WelcomeActivity extends Activity { @Override pr

  • Android UI组件AppWidget控件入门详解

    Widget引入 我们可以把Widget理解成放置在桌面上的小组件(挂件),有了Widget,我们可以很方便地直接在桌面上进行各种操作,例如播放音乐. 当我们长按桌面时,可以看到Widget选项,如下图所示: 点击上图中箭头处的widgets图标,会出现如下界面:(都是widget) 长按上图中的任意一个widget,就可以将其放到桌面上. Widget的使用 Widget的实现思路  (1)在AndroidManifest中声明AppWidget:  (2)在xml目录中定义AppWidget

  • Android如何实现APP自动更新

    先来看看要实现的效果图: 对于安卓用户来说,手机应用市场说满天飞可是一点都不夸张,比如小米,魅族,百度,360,机锋,应用宝等等,当我们想上线一款新版本APP时,先不说渠道打包的麻烦,单纯指上传APP到各大应用市场的工作量就已经很大了,好不容易我们把APP都上传完了,突然发现一个会导致应用闪退的小Bug,这时那个崩溃啊,明明不是很大的改动,难道我们还要再去重新去把各大应用市场的版本再上传更新一次?相信我,运营人员肯定会弄死你的!! 有问题,自然就会有解决问题的方案,因此我们就会想到如果在APP里

  • android app进行代码混淆实例详解

    接到一个新的任务,对现有项目进行代码混淆.之前对混淆有过一些了解,但是不够详细和完整,知道有些东西混淆起来还是比较棘手的.不过幸好目前的项目不是太复杂(针对混淆这块来说),提前完成--现总结之. 第一部分 介绍下操作流程(eclipse): 1.打开混淆器:找到项目根目录下的project.properties文件,将"#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt"

  • Android App增量更新详解及实例代码

    Android App增量更新实例--Smart App Updates        介绍 你所看到的,是一个用于Android应用程序增量更新的开源库. 包括客户端.服务端两部分代码. 原理 自从 Android 4.1 开始,Google引入了应用程序的增量更新. Link: http://developer.android.com/about/versions/jelly-bean.html Smart app updates is a new feature of Google Pla

  • 详解Android冷启动实现APP秒开的方法

    一.前言 在阅读这篇文章之前,首先需要理解几个东西: 1.什么是Android的冷启动时间? 冷启动时间是指用户从手机桌面点击APP的那一刻起到启动页面的Activity调用onCreate()方法之间的这个时间段. 2.在冷启动的时间段内发生了什么? 首先我们要知道当打开一个Activity的时候发生了什么,在一个Activity打开时,如果该Activity所属的Application还没有启动,那么系统会为这个Activity创建一个进程(每创建一个进程都会调用一次Application,

  • Android App应用启动分析与优化

    app的启动方式:  1.)冷启动  当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动.冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量.布局.绘制),最后显示在界面上.  2.)热启动  当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程

  • Android APP启动方式、启动流程及启动优化分析

    本文章向大家介绍Android app应用启动的一些相关知识,包括app启动方式.app启动流程和app启动优化等知识!  app应用启动方式 1.冷启动 当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动.冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量.布局.绘制),最后显示在界面上. 2.热启动 当启动应用时,后台已有该应用的进程(例:按

随机推荐