Android 源码:xml文件是如何加载到屏幕上的
在Android中,我们的布局通常是写在xml中的,然后通过setContentView这个方法,就能将布局加载到对应的Activity中,那么这个过程中做了哪些事了,代码流程是咋样的呢?今天就从setContentView这个方法说起,看看xml文件是如何加载到屏幕上的。
首先从activity的setContentView出发:
![](https://img.haomeiwen.com/i4471798/7e45dd4c55fb73dd.png)
通过这两个方法,我们可以看到它首先是拿到了一个mWindow对象,然后调用这个mWindow的setContentView方法。而Window是一个抽象类,它仅有一个实现类,那就是PhoneWindow。
![](https://img.haomeiwen.com/i4471798/86d008789884a6a8.png)
![](https://img.haomeiwen.com/i4471798/3b501f0af14945b6.png)
可见,在PhoneWindow中,首先会判断mContentParent是否为Null,如果为Null,那么会调用installDecor(),如果不为空,那么将移除所以子View进行复用。
![](https://img.haomeiwen.com/i4471798/261948fae6724ffb.png)
在installDecor方法中,首先会判断mDeco是否为Null,如果mDecor对象也为Null,那么就会调用generateDecor方法。
![](https://img.haomeiwen.com/i4471798/98b7d255d6572413.png)
在generateDecor方法中,会创建一个PhoneWindow的内部类DecorView对象
![](https://img.haomeiwen.com/i4471798/755421524bf830da.png)
在创建完DecorView之后,会对DecorView的属性做一些设置;然后再判断mContentParent是否为Null,如果mContentParent为Null,那么把刚刚创建的mDecor作为参数传入,调用generateLayout方法。
![](https://img.haomeiwen.com/i4471798/7ff8cf15769aa59f.png)
generateLayout这个方法主要是对Window的一些属性进行设定。首先,它会拿到我们在xml中设置的Window属性:
TypedArray a = getWindowStyle(); 这是位于Window的一个方法
![](https://img.haomeiwen.com/i4471798/ea4418e8b50b997f.png)
![](https://img.haomeiwen.com/i4471798/ca347f640675cd0a.png)
然后再对这些属性进行实现,比如我们经常用的Window_windowNoTitle Window_windowFullscreen属性:它会通过requestFeature和setFlags保存起来。将所有属性保存完之后,
![](https://img.haomeiwen.com/i4471798/de9e6a247a2b60a5.png)
![](https://img.haomeiwen.com/i4471798/0160a954df074fec.png)
可以看下翻译:明白就会通过Window抽象类中的getLocalFeatures方法取出来。比如
![](https://img.haomeiwen.com/i4471798/7568371b67753972.png)
然后加载对应的xml布局文件,加载完对应的样式布局之后,就开始给DecorView添加布局了。
![](https://img.haomeiwen.com/i4471798/d76ffece21160c6d.png)
首先mDecor.startChanging(); 就是给mChanging变量赋值为true。
然后会将我们之前拿到的xml文件填充出来,并且通过addView添加到decor中。
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
其中LayoutParams我们可以看到都是MATCH_PArent。然后填充的View会被认为是mContentRoot,这也是我们之前需要被加载的xml文件,id也就是content的布局。最后会将我们构建的contentParent返回:同时mDecor.finishChanging(),就是给mChanging变量赋值为false。
![](https://img.haomeiwen.com/i4471798/72286be55c94b85b.png)
上述就是installDecor的主要过程,后面执行的是:
![](https://img.haomeiwen.com/i4471798/6bb0b7d9fe2cbdcd.png)
首先判断FEATURE_CONTENT_TRANSITIONS这个值,看翻译是说安卓页面跳转的一些过渡动画,如果没有就执行LayoutInflater的inflate方法,并且把我们写的布局的layoutResId和mContentParent作为对象传入。
![](https://img.haomeiwen.com/i4471798/f1b6f910cd17eb99.png)
installDecor的末尾部分调用了LayoutInflater的inflate方法,最后会调用如下方法:
![](https://img.haomeiwen.com/i4471798/8e0aadde72f67c22.png)
这里会将传进来的resource通过getLayout方法拿到一个对应的xml解析器parser,然后再调用inflate方法:
![](https://img.haomeiwen.com/i4471798/ca2136b14a198abd.png)
1 首先会拿到xml布局中的属性
2 然后不断地循环,找到xml布局中的根节点
3 然后拿到根节点的名字
![](https://img.haomeiwen.com/i4471798/9ecfab8f94c40ea3.png)
如果这个根节点不是meage,那么就会生成对应的根节点。
然后通过root的generateLayoutParams方法,拿到对应的属性,设置到刚生成的根节点布局中。
这个root就是开始传过来的contentParent对象,而根节点就是我们自己写的最外面布局,比如RelativeLayout.。然后通过contentParent来解析RelativeLayout的对象属性,。通过setLayoutParams给RelativeLayout设置上。