Activity中setContentView()方法是怎么将页面加载出来的?

源码分析基于Android 29进行;

作为一名Android开发者,我们打开一个页面一般是在Activity中使用startActivity()方法就可以了。熟悉Android都知道Activity作为页面的基本载体,调用了startActivity()方法,就会执行Activity中的attach()->onCreate()->onResume()方法。具体怎么个执行流程,不是本次讨论的重点,感兴趣的同学可以阅读下关于Activity启动流程的文章。下面我们回归正题。

对于一个Activity页面而言,一般我们会在onCreate()方法里面设置setContentView()方法,然后当执行了onResume()方法后页面的布局就变得可见了,我们接下来就从源码的角度分析一下Activity是怎么通过执行setContentView()方法将页面加载出来的。

首先,我们看一下Activity的setContentView()方法的源码:

从上述代码看出是通过执行window的setContentView()方法将布局文件加载出来的,而这里的getWindow()方法获取的就是Window对象mWindow.那么这里的mWindow对象是什么时候创建的呢?上面说了Activity#attach()方法在onCreate()方法之前执行,我们看一下源码:

首先是mWindow对象是PhoneWindow的实例,同时PhoneWindow也是Window唯一实现类。在PhoneWindow里,我们看到存在一个DecorView对象实例mDecor,它就是Window的根View,另外还有一个ViewGroup#mContentParent控件需要我们关注。下面我们就分析下PhoneWindow#setContentView()的源码:

上面的源码我们看到,当mContentParent控件为空的时候,会调用installDecor()方法进行mDecor和mContentParent的初始化,当mContentParent控件不为空的时候,如果该window未设置“FEATURE_CONTENT_TRANSITIONS”属性,那么mContentParent将会移除所有的子控件。如果该Window设置了“FEATURE_CONTENT_TRANSITIONS”属性,那么将使用TransitionManager将layoutResId布局加入到mContentParent根布局中,否则则通过inflate方法加载布局。加载完成之后,通过回调告知Window窗口content发生变化。到此,布局文件就加载到Window里面了。

到这里我们梳理一下,我们使用的Activity里面是一个Window,也就是唯一实例PhoneWindow,同时在Window里面会创建一个DecorView。下面我们看一下它是怎么初始化根布局的呢?这就需要阅读installDecor()方法了。

从上述源码中我们能看出mDecor是通过generateDecor()进行创建,mContentParent是通过generateLayout()方法进行创建。具体的创建逻辑,让我们先看一下mDector的创建过程:

上述源码中我们看出这里是直接new出来一个DecorView控件。

下面我们来看一下generateLayout()方法的源码:

该方法主要是将系统的各种主题样式设置到DecorView中,同时通过DecorView#findViewById()获取contentParent控件,也就是PhoneWindow的成员变量mContentParent控件。现在回顾一下setContentView()方法你就明白了,布局就是这么添加进mContentParent中的。

上面讲述了布局文件是怎么加载进来的,也就是Activity#setContentView()的源码逻辑。熟悉Android开发的都知道,Activity#onCreate()方法只是设置了布局文件,而Activity#onResume()才是布局由不可见变为可见的。下面我们就分析下布局是怎么由不可见变为可见的。

我们接下来看一下ActivityThread#handleResumeActivity()方法的相关源码:

位置1处通过performResumeActivity()方法获取ActivityClientRecord对象,该方法就是使Acticity切换为onResume状态,该方法的源码就不在贴出来了,关键点是r.activity.performResume(r.startsNotResumed,reason)代码的执行。performResume()方法位于Activity中,方法的调用就是通过mInstrumentation调用callActivityOnResume()方法,然后Activity#onResume()方法就会回调了。

在来看位置2处的代码,当Activity#mVisibleFromClient为True,也就是状态为可见时,调用了Activity#makeVisible()方法。该方法的代码很简洁:

也就是通过把DecorView的状态设置为可见,那么布局也就可见了,现在大家明白了为什么说onResume()方法的回调才是页面由不可见变为可见状态。

总结一下:

1.Activity,Window,DecorView三者之间的关系,可以用下图说明:

PhoneWindow是Window的唯一实现,也是Activity的载体,DecorView是在PhoneWindow中创建,DecorView一般包含两部分布局,TitleView和ContentView,ContentView是一个ViewGroup,可通过“android.R.id.content”获取;

2.Activity#setContentView中的布局是通过DecorView加载到mContentParent中的,也就是上图中的ContentView中。

3.Activity通过setContentView设置了布局并不可见,是DecorView设置可见之后,页面才由不可见状态变为可见,然后回调的onResume()方法。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心灵行者

你的鼓励是我最大的创作动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值