Android 启动卡顿原因查询

启动是一个什么样的过程?首先要对这个过程进行一个定义。考虑到进程是否存在,对启动时间有着明确的影响,将启动分为两种情况。

冷启动 :在进程不存在的情况下,从点击应用 Icon 到用户能看到界面所占用的时间。
热启动 :在进程依然存在的情况下,点击应用 Icon 到用户能看到对应的界面所用的时间。

知道这两个启动时间怎么定义后,需要将这个进行实际的测量。通过这个测量时间的获得,就能定性地去衡量启动过程的性能问题。性能越好,所有时间就会更少,反之就更多。


宏观测量 — Adb 命令的使用

那么接下来看看,具体怎么将这个测量过程落地。

Android adb 提供了很简单的测量方式,预先通过在 FrameWork 层中的 ActivityManagerService 等等中进行埋点的方式,这样可以获得对应的时间点。

adb shell am start -W [packageName]/[activityName]

这样执行后,可以得到如下图所示的样子。

am_start

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

final long startTime = SystemClock.uptimeMillis();
if (mWaitOption) {
    result = mAm.startActivityAndWait(null, null, intent, mimeType,
                null, null, 0, mStartFlags, profilerInfo, null, mUserId);
    res = result.result;
} else {
    res = mAm.startActivityAsUser(null, null, intent, mimeType,
            null, null, 0, mStartFlags, profilerInfo, null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();

上面是 WaitTime 的计算方式,其他时间的计算方式也请参考源码。不过就实际的使用而言,这种方式得出的时间,不够准确,误差较大。

在 API 19 后,ActivityManagerService 也通过日志的方式告诉我们 Activity 启动花了多少时间。

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

这个时间也可以作为高版本系统中的另一个宏观参考指标。

使用这种方式的话,不如在关键时间点记录相关的时间,这种方式会更加直观一点,粒度也会更加精细一些。


精确测量 — TraceView 的应用

大部分人都知道利用 TraceView 进行分析,通过 TraceView 提供的数据分析出对于哪些方法是造成耗时的缘故。这里还是要简单地介绍一下 TraceView,方便不了解的同学。

TraceView 位于 Android Devices Monitor 里面。可以在 Android Studio -》Tools -》Android 中找到 Devices Monitor。在 Monitor 中就可以打开对应的 Trace 文件,或者在这里进行 Trace 操作。下图展示的是一个 Trace 界面。

TraceView
TraceView 关键指标的讲解

在上图中的下部分,是每个函数相关的调用信息。左边是函数的名称,右边是其调用的各种统计信息。
Incl Cpu Time: 这个方法调用所占据的时间。
Excl Cpu Time: 这个方法本身执行的时间,例如这个方法里面调用了 a, b 两个方法,Excl 就是排除 a,b 两者占用的时间之后的时间。
Incl Real Time 等等与前者类似,一个是 Real time,一个是 CPU 时间,两个区别不大。
Calls + Recur Calls / Total: 函数的调用次数(包含了递归调用) / 总次数。通常用于判断一些方法是否出现这个异常。
Cpu Time / Call: 每个函数的平均调用时间,这个对一些常用方法的判断尤为重要。例如一个 RecyclerView 经常调用 getview 方法,那么我对这个方法里面的优化是否生效呢?显然不能看单个 getview 的耗时,而是平均每个 getview 方法的耗时。

TraceView 的粗略使用指南

在 Android Devices Monitor 中左侧选中对应的进程名,然后点击如箭头所指的按钮,按钮中红点将会变成黑点,在界面上进行相应的操作,然后再次点击该按钮。等待片刻后,界面上就会出现相应的 TraceView 界面,根据上文所述的指标意义,找出问题的症结。

Start Trace

如果是针对启动问题的话,使用这种方式就不太合理了,还没等你点击按钮,就已经执行过了。给大家一种技巧,

// 在 /sdcard 目录下记录 trace_file.trace 的文件, 开始 trace
Debug.startMethodTracing("trace_file");
// 结束 trace,并输出文件
Debug.stopMethodTracing();

在这种情况下,只要在启动的前后分别调用两个方法,就能在 sdcard 中找到对应的 trace 文件。后续再使用 adb pull 导出,并使用 Android Studio ,或者 Devices Monitor 打开文件,就能进行分析了。


官方的偷天换日

对于大型 APK 而言,优化并非那么简单,牵涉到很多模块,某些模块的初始化必须在此启动,在这种情况下,Google 官方教程推出了 Launch-Time Performance | Android Developers 这篇文章。这里简单地介绍下这种偷天换日的方案,让用户在体验上感觉应用快了那么一点点。

在启动 Activity 之间,系统会先生成一个空白的 Window,等到 Activity 的各类资源准备完毕后,将其放置到 Window 上去。偷天换日的方案就是在这个 window 上做手脚,先将 window 的背景替换成类似于主界面的背景,这样用户会以为此时界面已经在加载中了。等一段时间后,再将这个 Window 替换为实际的 Activity 界面。

首先指定一个背景图,就像下面的代码一样。

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
  <!-- The background color, preferably the same as your normal theme -->
  <item android:drawable="@android:color/white"/>
  <!-- Your product logo - 144dp color version of your app icon -->
  <item>
    <bitmap
      android:src="@drawable/product_logo_144dp"
      android:gravity="center"/>
  </item>
</layer-list>

再指定 Activity 的主题到这个 style 文件中。

<activity ...
  android:theme="@style/AppTheme.Launcher" />

最后,一定要记得在 Activity 的 onCreate 方法里进行 Style 替换。

public class MyMainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
     Make sure this is before calling super.onCreate
    setTheme(R.style.Theme_MyApp);
    super.onCreate(savedInstanceState);
     ...
  }
}

最好的方式还是优化代码结构,一劳永逸地解决问题,这样才能自豪地说,我就是快!


文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期:2016年11月9日

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,386评论 25 707
  • 请保持淡定,分析代码,记住:性能很重要。 启动时间优化 毫无疑问,应用的启动速度越快越好。 本文可以帮助你优化应用...
    Mupceet阅读 11,365评论 5 19
  • A同学的救世主(文/小弋妇人) A同学在异性中的知名度就像今年的高温一样持续上升,遍及各地。这不是谁都有的,有的...
    异地狗肉铺阅读 286评论 0 1
  • 窗外的铁皮雨棚如边角破损的铜锣被急骤的敲了几响,声音有些浑浊,还未听得仔细,一道惊艳的白从天边闪过,不由分说的闯进...
    古卯阅读 369评论 0 1