大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的?
要想回答这些问题,我们就需要对android的布局加载 与 布局绘制流程有所了解,
六、activity的布局加载流程:
源码追踪:
activity完成启动后会调用我们Activity的onCreate方法,执行其中的setContentView(@LayoutRes int layoutResID)
- public void setContentView(@LayoutRes int layoutResID) {
- getWindow().setContentView(layoutResID);
- initWindowDecorActionBar();
- }
调用getWindow获得mWindow的实例PhoneWindow,所以这里调用的其实是PhoneWindow的setConentView方法,
然后在phoneWindow的setContentView()方法中的installDecor()方法中的generateDecor()方法中初始化了mDector,mDector是一个DectorView对象,而DectorView继承与FrameLayout,所以这里的mDector其实就是一个FrameLayout对象。
在初始化mDector的installDecor()方法中,同时初始化mContentParent ,具体的初始化方法是 mContentParent= generateLayout(mDector),
在generateLayout(mDector)方法中{
-
- decor = mDector
- decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mContentRoot = (ViewGroup) in;
- ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
- return contentParent ;
}
通过对代码的分析,我们发现PhoneWindow中的几个成员变量:mDector,mContentRoot,mContentParent的关系
mDector –> mContentRoot –> mContentParent(包含)
在调用完setContentView()方法中的installDecor()方法后,接着调用mLayoutInflater.inflate(layoutResID, mContentParent);
这个方法的含义就是将我们传递的客户端的layoutId对应的布局文件作为mContentParent的子View加载到内存中,
这样我们的layoutId作为mContentParent的子View,而mContentParent又是mContentRoot的子View,mContentRoot又是mDector的子View,通过LayoutInflater的inflate方法逐步加载到了内存中,而我们的Activity又持有自身的PhoneWindow的引用,这就相当于我们的Activity持有了我们定义的布局文件的引用,因而Activity的布局文件被加载到了内存中。
源码分析结束。
0、其实Activity对界面布局的管理是都是通过Window对象来实现的,Window对象,顾名思义就是一个窗口对象,而Activity从用户角度就是一个个的窗口实例,因此不难想象每个Activity中都对应着一个Window对象,而这个Window对象就是负责加载显示界面的。至于window对象是如何展示不同的界面的,那是通过定义不同的View组件实现不同的界面展示。
1、通过对代码的分析,我们发现PhoneWindow中的几个成员变量:mDector,mContentRoot,mContentParent的关系
mDector –> mContentRoot –> mContentParent(包含)
2、将我们传递的客户端的layoutId对应的布局文件作为mContentParent的子View加载到内存中,这样我们的layoutId作为mContentParent的子View,而mContentParent又是mContentRoot的子View,mContentRoot又是mDector的子View,通过LayoutInflater的inflate方法逐步加载到了内存中,而我们的Activity又持有自身的PhoneWindow的引用,这就相当于我们的Activity持有了我们定义的布局文件的引用,因而Activity的布局文件被加载到了内存中。
总结:
-
Activity的展示界面的特性是通过Window对象来控制的;
-
每个Activity对象都对应这个一个Window对象,并且Window对象的初始化在启动Activity的时候完成,在执行Activity的onCreate方法之前;
-
每个Window对象内部都存在一个FrameLayout类型的mDector对象,它是Acitivty界面的root view;
-
Activity中的window对象的实例是PhoneWindow对象,PhoneWindow对象中的几个成员变量mDector,mContentRoot,mContentParent都是View组件,它们的关系是:mDector –> mContentRoot –> mContentParent –> 自定义layoutView
-
LayoutInflater.inflate主要用于将布局文件加载到内存View组件中,也可以设定加载到某一个父组件中;
-
典型的Activity的onCreate方法中需要调用super.onCreate方法和setContentView方法,若不调用super.onCreate方法,执行启动该Activity的逻辑会报错,若不执行setContentView的方法,该Activity只会显示一个空页面。
七、activity的布局绘制流程:
通过上一篇文章的分析,我们知道Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window只是控制着界面布局文件的加载过程,那么界面布局文件的绘制流程是如何的呢?这篇文章主要就是顺着上篇文章的思路,看一下在android系统中Activity的布局文件是如何绘制的。
源码追踪:
Android体系在执行Activity的onResume方法之前会回调ActivityThread的handleResumeActivity方法:
在handleResumeActivity方法中:执行了r.activity.makeVisible()方法
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
这里的ViewManager对象是通过getWindowManager()方法获取的,我们来看一下getWindowManager()方法的具体实现:
public WindowManager getWindowManager() {
return mWindowManager;
}
好吧,原来就是返回的Activity的mWindowManager的成员变量,那么这个mWindowManager的成员变量是什么时候赋值的呢?上一篇文章我们在Activity的attach方法方法中初始化了Activity的相关成员变量,这里也包括了mWindowManager,我们来看一下mWindowManager的赋值过程:
mWindowManager = mWindow.getWindowManager();
好吧,这里的Window.getWindowManager()方法是具体如何实现的呢?
public WindowManager getWindowManager() {
return mWindowManager;
}
那么这里的Window对象的mWindowManager成员变量是具体如何赋值的?
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
好吧,可以发现mWindowManager = ((WindowManagerImpl)vm).createLocalWindowManager(this)原来是在这里赋值的,所以一个Activity对应这一个新的Window,而这个Window对象内部会对应着一个新的WindowManager对象,我们接着往下看,那么createLoclWindowManager方法是如何实现的呢?
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
好吧,原来是new出了一个WindowManagerImpl对象,所以回到我们的Activity的makeVisible方法,ViewManager获取的是一个WindowManagerImpl对象,所以Window对象内部的WindowManager对象其实都是一个WindowManagerImpl的实例,都是而且从继承关系上可以看到:
WindowManagerImpl –> WindowManager –> ViewManager;
接着继续看
void makeVisible() {
}的方法中的<span style="color: rgb(51, 51, 51); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">wm</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box; font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">.addView</span><span style="color: rgb(51, 51, 51); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">(mDecor, getWindow()</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box; font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">.getAttributes</span><span style="color: rgb(51, 51, 51); font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">())</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box; font-family: 'Source Code Pro', monospace; font-size: 14px; line-height: 22.5556px; white-space: pre; background-color: rgba(128, 128, 128, 0.0470588);">;代码</span>
这里的mDector成员变量,通过上一篇文章的介绍,我们知道,它是Activity的界面根View,而getWindow.getAttrbutes方法是windowManager中定义的Params内部类,该内部类定义了许多的Window类型,由于这里的vm是WindowManagerImpl的实例,我们来看一下这里的addView的具体实现:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
。。。。。。。待续
总结:
-
Activity执行onResume之前,在ActivityThread中执行Activity的makeVisible方法。
-
View的绘制流程包含了测量大小,测量位置,绘制三个流程;
-
Activty的界面绘制是从mDector即根View开始的,也就是从mDector的测量大小,测量位置,绘制三个流程;
-
View体系的绘制流程是从ViewRootImpl的performTraversals方法开始的;
-
View的测量大小流程:performMeasure –> measure –> onMeasure等方法;
-
View的测量位置流程:performLayout –> layout –> onLayout等方法;
-
View的绘制流程:onDraw等方法;
-
View组件的绘制流程会在onMeasure,onLayout以及onDraw方法中执行分发逻辑,也就是在onMeasure同时执行子View的测量大小逻辑,在onLayout中同时执行子View的测量位置逻辑,在onDraw中同时执行子View的绘制逻辑;
-
Activity中都对应这个一个Window对象,而每一个Window对象都对应着一个新的WindowManager对象(WindowManagerImpl实例);
八、应用进程context的创建流程: