浅谈Activity 启动过程

引言:页面启动时间怎么计算?

在做启动优化的过程中,在做了一堆的优化工作之后,需要对整个的启动优化效果进行对比和评估,优化之后的启动时间和优化之前对比,真的变短了吗?之前是多少?现在是多少?

首先我们自己 从用户的角度 来定义以下Activity页面的加载时间。我认为页面的加载时间应该是: 从页面开始创建——页面渲染结束,对用户可见并且用户可操作 的时间。

那么,从开发者的角度 ,我们怎么测算这个加载时间呢?起初我以为页面的启动时间 = OnCreate 的时间 + onStart的时间,但通过打日志发现,这个时间往往较短,跟体验到以及监控到的时间不符。另外在onstart时间打出日志的时候,往往页面还并不可见。

onCreate + onStart的时间
Display日志

可以看出 ,onCreate开始到onStart结束,总时间为298 ms,而此时我们过滤系统打出的Displayed日志 ,发现达到了1111 ms,差距这么大! 

那么,Displayed日志,是什么时候打印出来的呢?

页面在onStart之后,800+ ms的时间里面,干了什么工作呢?

要回答这两个问题,我们先从Activity的启动过程说起。

页面启动流程第一步:Activity生命周期

为了了解Activity的启动入口,我在Activity的onResume中设置了一个断点,通过断点查看堆栈调用信息如下:

Activity启动堆栈

可以看到页面的启动从NativeStart 方法的 main() 函数开始,调用到ActivityThread的main() 函数,通过Looper 发送消息调用到 ActivityThread 的 handleLaunchActivity() 方法。

那么的 handleLaunchActivity 里面做些什么呢?可以看到先执行了performLaunchActivity方法,然后执行了handleResumeActivity方法:

ActivityThread 的 handleLaunchActivity 方法

performLaunchActivity 方法中,执行了Activity对象的创建,oncreate和onstart:

ActivityThread 的 performLaunchActivity 方法(1)
ActivityThread 的 performLaunchActivity 方法(2)

所以总结下来的,Activity的启动过程就一定会依次执行Activity的生命周期中的onCreate(),onStart()和onResume会依次执行,不可打断。

更加详细的启动过程见下图:

Activity启动流程

onresume执行之后,会开始进行Activity的绘制,将Activity的dectorView attach 到window上,此后开始界面的渲染绘制。

因此,根据我们一般的开发习惯,在oncreate中执行setContentView,调用了xml的inflate方法,并不意味着布局文件已经渲染完毕,LayoutInflater 进行 inflate的过程,仅仅是将布局文件中定义的 view 元素通过调用createViewFromTag方法实例化的过程。setContentView实现了布局文件到java对象的转换,却并没有开始渲染和绘制。

inflate过程源代码

因此,在Activity的onresume执行完毕之后,Activity的界面UI在刚刚开始渲染,整个的页面对用户并不可见。这就是为什么onCreate~onStart 或者 onResume 整体的时间往往远远小于页面加载的时间(页面对用户可见并可操作)。

页面启动流程第二步:页面渲染

我们认为Activity启动结束的时间应该是这个页面绘制完毕的时间点,前文说到在setContentView中仅仅是完成了布局文件的实例化,并没有开始渲染,真正渲染的部分是在Activity 的onResume之后,那么view的绘制具体是在什么时候被调用,什么时候执行的呢?

首先通过日志看下现象:

activity的生命周期与view的绘制

从日志可以看出,view的绘制发生在activity attachToWindow之后。

Activity的hadleResumeActivity的部分代码如下,可以看到在执行完performResumeActivity(包括了Activity的onResume回调)之后,执行了WindowManagerImpl的addView方法,继续追踪,执行到了WindowManagerGlobal 的addView方法。之后的流程便如下面的图所示,通过requestLayout依次调用了ViewRootImpl 的 scheduleTraversals-》TraversalRunnable-》doTraversal-》performTraversals。在performTraversals中就包括了performMeasure,performLayout,performDraw的过程。

view 开始绘制的入口

如上,view的绘制包括三个步骤:

onMeasure:测量大小

onLayout:计算摆放的位置

onDraw:绘制过程

上面的三个步骤均是通过递归的形式逐步完成的,从父View开始,逐级向子view传递,最终完整整个view tree的绘制。view tree的基本结构如下图:

view tree


回到主题:启动时间的计算

现在,我们来回答文章开头提出的两个问题:

那么,Displayed日志,是什么时候打印出来的呢?

页面在onStart之后,800+ ms的时间里面,干了什么工作呢?

问题1:Displayed日志,是什么时候打印出来的呢?

以上,view draw过程结束后,一个完整的对用户可见的Activity总算是好了。理论上说,所有的view绘制结束,并且将网络请求回来的数据作用于Activity的界面,改变UI,实现我们期待的展示效果,就算是页面load完成了。

但是事实上系统并不能很准确地知道你的各个view是否已经渲染完成并且展示了正确的数据。因此系统采用了一个比较偷懒的方式,尽管不能完全准确,但可以作为评估Activity启动耗时的反映。

Displayed日志的打印是在ActivityRecord中打印出来的。具体的源码大家可以自行去查看。

在Activity中有一个方法,reportFullyDrawn,如下。

reportFullyDrawn

这个方法可以由用户在activity启动真正结束的地方调用,这样就可以获得更加准确的启动耗时,Displayed日志就回打印出真正结束的时间。

如果用户没有调用,系统默认采用的是window首次绘制的时间,如上面注释中所表达。

另外,为了实现更加准确的自动化无侵入的耗时监测,也可以通过Choreographer类,去坚挺的每一帧的绘制,计算已经完成绘制的view占Activity总view的数量的比例,如果超过了一定的比例,就认为绘制已经完成,也是可取的方法。

问题2: 页面在onStart之后,800+ ms的时间里面,干了什么工作呢?

这个问题大家应该自然而然明白了吧,前面说到,Activity的启动过程经过了onCreate,onStart,onResume,但是这个过程还并没有开始View的绘制,所以,onResume之后的一大块时间,大多是完成页面View的measure,layout和draw去啦。

感谢看到这里,希望对你有帮助~~

参考文章:

Android Activity启动过程

Android Activity.startActivity流程简介

View的生命周期方法和Activity生命周期方法关系

Android应用层View绘制流程与源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容