Android源码:View是如何绘制到屏幕上的?

前言

Android中如果要显示一个页面,那么只需要在ActivityonCreate()方法中,把我们写好的页面布局layout传入到setContentView()方法中即可,这样Activity就会完成接下来的工作,通过一系列的操作把我们想要的布局页面显示出来,不需要我们再做任何的处理,所以最初的时候一直以为是Activitylayout布局中的控件绘制出来的,但事实究竟是怎样的呢,我们来一探究竟。

首先,我们都知道在onCreate()方法中调用setContentView()函数,Activity并不会立刻将页面显示出来,而是在执行到ActivityonResume()生命周期之后才会将Activity显示出来。所以针对于这个问题 “View是如何绘制到屏幕上的?” ,可以从以下两个方面来进行思考。

  • 调用setContentView之后都做了哪些事?
  • onResume这个生命周期里也就是Activity被显示之前做了哪些事?

Ok,Let’s go~

1、Activity的setContentView()

我们从ActivitysetContentView()入手:

1.png

可以看到在ActivitysetContentView()方法中,调用了getWindow()setContentView()Activity在第一时间就将它交到了getWindow()的手里,getWindow()返回了一个Window的对象,Window是一个抽象类,PhoneWindow是它唯一的实现类,并且从代码中我们可以得知mWindow这个对象是在Activityattach()方法中进行创建的。(attach()这个方法很重要,很多跟Activity相关的重要信息都是在这个方法中进行初始化的,尤其是初始化了和显示相关的信息,是我们需要重点关注的)此时我们的布局如下:

image.png 我们接着往下看,进入PhoneWindowsetContentView()中。

image.png

phoneWindowsetContentView()中,主要做了两件事:

  • installDecor:如果mContentParentnull,就会在这个方法中创建DecorViewContentParent
  • inflate:通过inflate的方式将我们在Activity中传入的xml布局文件也就是R.layout.activity_main,转换成了树形结构的View,并且把ContentParent作为父节点。

installDecor

8.jpg

这里我们把重点部分用红框圈了起来,首先在installDecor()方法中,通过generateDecor()创建出了DecorView,以及通过generateLayout()创建出了ContentParent

  • generateDecor:创建DecorViewDecorView继承自FrameLayout,此时为我们的布局中创建出了DecorView,布局如下:

image.png

  • generateLayout:创建ContentParent,在创建的过程中会根据不同的feature创建不同的系统布局。

这里我们主要来了解一下ContentParent的创建过程,首先会根据不同的的feature创建不同的系统布局,这里的feature较多,我们挑一个最简单也是最普遍的R.layout.screen_simple为例,xml文件代码如下:

9.png

R.layout.screen_simple中是由ViewStubFrameLayout两个部分组成,进入ViewStublayout布局能够发现其实就是一个ActionBar;这里注意下FrameLayoutidcontent,后面会用到。了解了screen_simple中的内容后,接下来我们看mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)这行代码,在onResourcesLoaded()中先是将布局id转换成View,然后再通过addView将此View添加到DecorView中。onResourcesLoaded()代码如下:

10.png

onResourcesloaded()之后呢又通过findViewById(ID_ANDROID_CONTENT)查找到了ContentParent,并且将ContentParent返回,这个ID_ANDROID_CONTENT就是 com.android.internal.R.id.content,也就是我们上面提到的R.layout.screen_simple布局文件中idcontentFrameLayout,所以最终将screen_simple中的布局控件添加到DecorView中之后,此时的布局如下:

image.png

此时generateLayout()方法就结束了,我们再次返回到PhoneWindowsetContentView()方法中的第部分,可以看到通过inflate的方式将我们从Activity中传入的布局R.layout.activity_main加载到了mContentParent,而这个mContentParent就是我们上一步通过generateLayout()创建出来的ContentParentimage.png

所以将activity_main添加到ContentParent之后的布局如下:

image.png

至此,Activity需要显示的内容已经被初始化完成了,但是此时Activity并不是可见的,直到ActivityonResume()阶段才会将PhoneWindow中的DecorView真正的绘制到屏幕上,我们对setContentview()进行一个总结,至于onResume()的分析我们稍后继续。

setContentView总结:

Activity实例创建好并且执行attach()方法的时候,会为Activity创建一个PhoneWindow,接下来就到了onCreate()的生命周期,在调用setContentView()的时候,如果还没有初始化ContentParent,说明是第一次进行setContentView(),那么就会初始化DecorView,还会给DecorView添加一个系统页面样式的子View(R.layout.screen_simple),那么在系统样式的ViewGroup中,就可以通过id找到用来加载自定义布局的ContentParent,再通过inflate就可以将我们自己写的xml文件(R.layout.main)转化为一颗ViewTree了,这颗ViewTree就在ContentParent里面。

所以setContentView()的作用最终可以总结为:

  • 创建DecorView
  • 创建ContentParent
  • 自定义布局转化为ViewTree,放在ContentParent

接下来让我们继续了解一下ActivityonResume()OK,Let’s go~

2、Activity的onResume

ActivityonResume()生命周期是在ActivityThread中的handleResumeActivity()方法中执行的,在这个方法中通过performResumeActivity()触发了onResume()的回调。

image.png

可以看到触发onResume()回调之后,在Activity未关闭并且即将要显示的条件下,先是获取了ActivityDecorView,然后又获取了WindowManager,最后呢再调用了WindowMangeraddView()方法将DecorView添加到WindowManager中,我们来看下这个WindowManager是在Activityattach()中通过setWindowManager创建出来的WindowManagerImpl对象,每一个Activity都会对应一个WindowManager对象,一层一层的进入addView()最终代码来到了WindowMangerGlobaladdView(),如下图所示:

5.png

WindowManagerGlobal是一个单例,在它的addView()方法中如果是首次添加的话就会创建了一个ViewRootImpl,然后将DecorView添加到ViewRootImpl中。

6.png

ViewRootImplsetView()中,首先会调用requestLayout()触发布局的绘制流程,我们熟悉的measurelayoutdraw的绘制流程就是从这里开始的,这一步可以确保View被添加到屏幕上之前已经完成了测量和绘制操作。然后会调用mWindowSessionaddToDisplayAsUser()通知WMS添加窗口, 这里就涉及到了跨进程通信,方法的最后把decorparent设置为了ViewRootImpl,这样做的目的就是让ViewRootImpl能够管理整个ViewTree。 接下来我们就来看一下mWindowSession这个对象。

7.png

mWindowSessionWindowManagerGlobal中的一个单例对象,它是IWindowSession类型并且继承自IBinder,它的实现类是System进程中的Session,可以看到它是通过WMS进程的openSession来获取的,至此剩下的工作就交由WMS进程来进行后续的添加工作了。

onResume 总结:

onResume()中会调用WindowManager中的addView()添加DecorView,当WindowManager管理ViewTree的时候会给ViewTree分配一个ViewRootImplViewRootImpl的第一个作用就是管理ViewTree的绘制工作,包括显示、测量,同步刷新以及事件分发等等,第二个作用就是负责与其他的服务进行通信。在同时存在多个Activity的情况下,每个Activity都有自己的PhoneWindowDecorView以及WindowManagerImplWindowManagerGlobal持有每个ActivityRootViewmWindowSessionmWindow是用来和WMS进行双向通信的。

3、总结

image.png

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值