Android 中如何计算 App 的启动时间?
(转载)
已知的两种方法貌似可以获取,但是感觉结果不准确:一种是,adb shell am start -w packagename/activity,这个可以得到两个值,ThisTime和TotalTime,不知道两个有什么区别,而且与实际启动时间不匹配,两者相加都可能比实际启动时间小(测试游戏的时候差别更大);另外一种是通过adb logcat的方式,感觉获取的结果也与实际有差别
1 应用启动场景
事实上 Android 中一个 App 的启动时间可以准确计算的.但是要分场景.也就是说要分开游戏和应用. 大家都知道,在Android中,游戏开发和应用开发是两码事.所以我们需要分开来说.
1.1 应用启动
我们平时在写应用的时候,一般会指定一个 mainActivity ,用户在桌面上点击这个 Activity 的时候,系统会直接起这个 Activity. 我们知道 Activity 在启动的时候会走 onCreate/onStart/onResume .这几个回调函数.
许多书里讲过,当执行完 onResume 函数之后,应用就显示出来了…其实这是一种不准确的说法,因为从系统层面来看,一个 Activity 走完 onCreate/onStart/onResume 这几个生命周期之后,只是完成了应用自身的一些配置,比如 window 的一些属性的设置/ View 树的建立(只是建立,并没有显示,也就是说只是调用了 inflate 而已) . 后面 ViewRootImpl 还会调用两次performTraversals ,初始化 Egl 以及 measure/layout/draw. 等.所以我们定义一个 Android 应用的启动时间, 肯定不能在 Activity 的回调函数上下手.而是以用户在手机屏幕上看到你在 onCreate 的 setContentView 中设置的 layout 完全显示为准,也就是我们常说的应用第一帧.
上面扯得有点远,不感兴趣的话可以不看,下面直接说方法.题主说的 adb shell am start -w packagename/activity,是可以完全应用的启动时间的.不过也要分场景.
1.2 应用第一次启动
也就是我们常说的冷启动,这时候你的应用程序的进程是没有创建的. 这也是大部分应用的使用场景.用户在桌面上点击你应用的 icon 之后,首先要创建进程,然后才启动 MainActivity.这时候adb shell am start -w packagename/MainActivity 返回的结果,就是标准的应用程序的启动时间(注意 Android 5.0 之前的手机是没有 WaitTime 这个值的):
➜ adb shell am start -W com.meizu.media.painter/com.meizu.media.painter.PainterMainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.meizu.media.painter/.PainterMainActivity }
Status: ok
Activity: com.meizu.media.painter/.PainterMainActivity
ThisTime: 355
TotalTime: 355
WaitTime: 365
Complete
总共返回了三个结果,我们以 WaitTime 为准.
关于ThisTime/TotalTime/WaitTime的区别,下面是其解释:
“adb shell am start -W ”的实现在 frameworks\base\cmds\am\src\com\android\commands\am\Am.java 文件中。其实就是跨Binder调用ActivityManagerService.startActivityAndWait() 接口(后面将ActivityManagerService简称为AMS),这个接口返回的结果包含上面打印的ThisTime、TotalTime时间.

- startTime记录的刚准备调用startActivityAndWait()的时间点
- endTime记录的是startActivityAndWait()函数调用返回的时间点
- WaitTime = startActivityAndWait()调用耗时。
ThisTime、TotalTime 的计算在 frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java 文件的 reportLaunchTimeLocked() 函数中。

我们来解释下代码里curTime、displayStartTime、mLaunchStartTime三个时间变量.
- curTime表示该函数调用的时间点.
- displayStartTime表示一连串启动Activity中的最后一个Activity的启动时间点.
- mLaunchStartTime表示一连串启动Activity中第一个Activity的启动时间点.
正常情况下点击桌面图标只启动一个有界面的 Activity,此时 displayStartTime 与mLaunchStartTime 便指向同一时间点,此时 ThisTime=TotalTime。另一种情况是点击桌面图标应用会先启动一个无界面的 Activity 做逻辑处理,接着又启动一个有界面的Activity,在这种启动一连串 Activity 的情况下(知乎的启动就是属于这种情况),displayStartTime 便指向最后一个 Activity 的开始启动时间点,mLaunchStartTime 指向第一个无界面Activity的开始启动时间点,此时 ThisTime!=TotalTime。这两种情况如下图:

在上面的图中,我用①②③分别标注了三个时间段,在这三个时间段内分别干了什么事呢?
- 在第①个时间段内,AMS 创建 ActivityRecord 记录块和选择合理的 Task、将当前Resume 的 Activity 进行 pause
- 在第②个时间段内,启动进程、调用无界面 Activity 的 onCreate() 等、 pause/finish 无界面的 Activity
- 在第③个时间段内,调用有界面 Activity 的 onCreate、onResume
看到这里应该清楚 ThisTime、TotalTime、WaitTime 三个时间的关系了吧。WaitTime 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;ThisTime 表示一连串启动 Activity 的最后一个 Activity 的启动耗时;TotalTime 表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用 Activity pause 的耗时。也就是说,开发者一般只要关心 TotalTime 即可,这个时间才是自己应用真正启动的耗时。
Event log中 TAG=am_activity_launch_time 中的两个值分表表示 ThisTime、TotalTime,跟通过 “adb shell am start -W ” 得到的值是一致的。
最后再说下系统根据什么来判断应用启动结束。我们知道应用启动包括进程启动、走 Activity生命周期 onCreate/onResume 等。在第一次 onResume 时添加窗口到WMS中,然后measure/layout/draw,窗口绘制完成后通知 WMS,WMS 在合适的时机控制界面开始显示(夹杂了界面切换动画逻辑)。记住是窗口界面显示出来后,WMS 才调用reportLaunchTimeLocked() 通知 AMS Activity 启动完成。
最后总结一下,如果只关心某个应用自身启动耗时,参考TotalTime;如果关心系统启动应用耗时,参考WaitTime;如果关心应用有界面Activity启动耗时,参考ThisTime。
1.2 应用非第一次启动
如果是你按Back键,并没有将应用进程杀掉的话,那么执行上述命令就会快一些,因为不用创建进程了,只需要启动一个Activity即可。这也就是我们说的应用热启动。
2 游戏启动场景
游戏启动的话,就不适用用命令行的方法来启动了,因为从用户点击桌面图标到登录界面,既有系统的部分也有游戏自己的部分。
2.1 系统部分
游戏也有一个Activity,所以启动的时候还是会去启动这个Activity,所以系统启动部分也就是用户点击桌面桌面响应到这个Activity启动。
2.2 游戏部分
一般游戏的主Activity启动后,还会做一些比较耗时的事情,这时候你看到的界面是不能操作的,比如:加载游戏数据、联网更新数据、读取和更新配置文件、游戏引擎初始化等操作。从游戏开发的角度来看,到了真正用户能操作的界面才算是一个游戏真正加载完成的时间。那么这个时间,就得使用Log来记录了,因为加载游戏数据、联网更新数据、读取和更新配置文件、游戏引擎初始化这些操作,都是游戏自己的逻辑,与系统无关,所以得由游戏自己定义加载完成的点。
对于游戏的启动时间,我们更倾向于计算从 点击桌面图标 到 用户可以与游戏进行交互 这个时间段作为一个游戏的启动时间。
3 总结
计算机最让人着迷的一点就是其准确性,1+1永远等于2,启动耗时多久就是多久,每一次可能不一样,但每一次的时间都是这一次的准确时间。
不过每个公司由于对应用的定位不同,所以对应用启动的要求也不一样。比如有的做 ROM 的公司,其内置应用的启动时间一定是要非常快的,这样给用户的第一感觉就是快、流畅;互联网公司的 App 则不是很关心启动速度,大部分互联网公司的应用都有一个启动页,用来展示广告或者功能介绍之类的,然后才会进入到主界面。需求不一样,这么做也无可厚非,不过从消费者的角度来看,越早见到主界面当然越好。
所以在做一个 Android App 的时候,一定要记得将应用的启动时间作为一个性能指标,毕竟
天下武功,唯快不破!
Android 中如何计算 App 的启动时间?的更多相关文章
- 如何计算 App 的启动时间
应用启动场景 事实上 Android 中一个 App 的启动时间可以准确计算的.但是要分场景.也就是说要分开游戏和应用. 大家都知道,在Android中,游戏开发和应用开发是两码事.所以我们需要分开来 ...
- Android中为APP创建快捷方式的原理(自己的理解)
我们首先来看Android中为APP创建快捷方式的原理: 从图上可以看出,Android大致分7步完成快捷方式的创建: 第一步:Android系统的launcher程序会调用它的pickShortcu ...
- Android中实现APP文本内容的分享发送与接收方法简述
谨记(指定选择器Intent.createChooser()) 开始今天的内容前,先闲聊一下: (1)突然有一天头脑风暴,对很多问题有了新的看法和见解,迫不及待的想要分享给大家,文档已经写好了,我需要 ...
- Android中实现app版本更新
1,获取本地程序apk版本,并开启服务(下面这段代码一般在主Activity中的onCreate()方法中执行的,并开启后台服务下载新版本的apk) //获取apk包文件的管理者对象 PackageM ...
- Android测试(一):在Android中测试App
原文:https://developer.android.com/training/testing/index.html 测试你的App是开发过程中的重要组成部分.通过对应用程序持续的运行测试,你可以 ...
- 在Android中使App高速、简单地支持新浪微博、微信、QQ、facebook等十几个主流社交平台的分享功能
前言 在如今的APP或者游戏中,分享功能差点儿已经成为标配.分享功能不但能够满足用户的需求.也能够为产品带来很多其它的用户,甚至能够对用户的行为.活跃度.年龄段等情况进行数据统计,使得软件公司能够对产 ...
- 关于Android中查看app安装时间等信息的问题
PackageManager packageManager=this.getPackageManager(); try { PackageInfo packageInfo=packageManager ...
- 抓包工具Fidder详解(主要来抓取Android中app的请求)
今天闲着没吊事,来写一篇关于怎么抓取Android中的app数据包?工欲行其事,必先利其器,上网google了一下,发现了一款神器:Fiddler,这个貌似是所有软件开发者必备神器呀!这款工具不仅可以 ...
- Android中一张图片占据的内存大小是如何计算
本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家发布 最近封装了个高斯模糊组件,正好将图片相关的理论基础也梳理了下,所以,这次就来讲讲,在 Android 中,怎么计算一张图片在 ...
随机推荐
- Hibernate学习笔记3
---恢复内容开始--- 一.hibernate如何转化jdbc代码实例[通过hibernate构建jdbc后往数据库传对象] import java.sql.Connection;import ja ...
- 转:ibatis的cacheModel
转:ibatis的cacheModel cachemodel是ibatis里面自带的缓存机制,正确的应用能很好提升我们系统的性能. 使用方法:在sqlmap的配置文件中加入 <cacheMode ...
- Deepin -Android Studio-Genymotion 之旅
Deepin -Android Studio-Genymotion 之旅 暑假无聊中在deepin系统下配置好了android的开发环境,并使用了比较好的一款模拟器–Genymotion;下面是我配置 ...
- 二、jquery选择器
在jquery库中,可以通过选择器实现DOM元素快捷选择这一重要的核心功能. 1.选择器的优势 (1)代码更简单 由于在jquery库中,封装了大量可以通过选择器直接调用的方法或函数,使编写代码更加简 ...
- js--敏感词屏蔽
<!doctype html><html><head><meta charset="utf-8"><meta name=&qu ...
- Delphi字符串简码
从网上找的三个函数自己修改了下,在delphi7运行正常,unicode的版本不能用好像 输入:1abc天天 输出:1ABCTT unit UnitJM; interface uses SysUtil ...
- CentOS下通过yum安装svn及配置
CentOS下通过yum安装svn及配置 1.环境centos5.5 2.安装svnyum -y install subversion 3.配置 建立版本库目录mkdir /www/svndata s ...
- RunLoop相关知识的总结
RunLoop 即运行循环,也叫事件循环,本质为一个死循环.iOS一个程序运行起来之后,默认会开启一个运行循环,有需要处理的操作时,比如用户的输入事件时,RunLoop会自己跑起来运行,没有需要处理的 ...
- 《Linux及安全》实践2
<Linux及安全>实践2 [edited by 5216lwr] 一.Linux基本内核模块 1.1理解什么是内核模块 linux模块是一些可以作为独立程序来编译的函数和数据类型的集合. ...
- target-densitydpi
一个屏幕像素密度是由屏幕分辨率决定的,通常定义为每英寸点的数量(dpi).Android支持三种屏幕像素密度:低像素密度,中像素密度,高像素密度.一个低像素密度的屏幕每英寸上的像素点更少,而一个高像素 ...