1 参考链接
Android应用程序窗口(Activity)的视图对象(View)的创建过程分析
Android应用setContentView与LayoutInflater加载解析机制源码分析
Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析
2 概念
每一个Activity都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。应用程序窗口视图是真正用来实现UI内容和布局的,也就是说,每一个Activity组件的UI内容和布局都是通过与其所关联的一个Window对象的内部的一个View对象来实现的。
由图可知:Activity、 Window、 View 三者如何协同显示界面的。一个Activity包含一个window对象,这个对象是由PhoneWindow来实现的,PhoneWindow将DecorView做为整个应用窗口的根View,而这个DecorView又将屏幕划分为两个区域一个是TitleView一个是ContentView,我们平常做应用所写的布局正是展示在ContentView中的。
通俗的表达是:Activity剪窗花的人(控制的);Window窗户(承载的一个模型);View窗花(要显示的视图View);LayoutInflater剪刀—将布局(图纸)剪成窗花。
3 UML图分析
(1)应用程序窗口内部所包含的视图对象的实际类型为DecorView。DecorView类继承了View类,是作为容器(ViewGroup)来使用的,它的实现如图1–DecorView类的实现所示:
(2)每一个应用程序窗口的视图对象都有一个关联的ViewRootImpl对象,这些关联关系是由窗口管理器来维护的,如图2–应用程序窗口视图与ViewRootImpl的关系图所示:
ViewRootImpl类是从Handler类继承下来的,从Handler类继承下来的子类可以调用父类的成员函数sendMessage来向指定的线程的消息队列发送消息,以及在自己重写的成员函数handleMessage中处理该消息。 ViewRootImpl类在两种情况需要经常应用程序进程的主线程的消息队列发送消息。
简单来说,ViewRootImpl相当于是MVC模型中的Controller,它有以下职责:
- 负责为应用程序窗口视图创建Surface。
- 配合WindowManagerService来管理系统的应用程序窗口。
- 负责管理、布局和渲染应用程序窗口视图的UI。
(3)Activity组件在启动的过程中,会调用ActivityThread类的成员函数handleLaunchActivity,用来创建以及首次激活Activity组件,因此,接下来我们就从这个函数开始,具体分析应用程序窗口的视图对象及其所关联的ViewRootImpl对象的创建过程,如图3–应用程序窗口视图的创建过程所示。
Activity第一次setContentView时,performLaunchActivity()开始做的操作是初始化各个view对象, 并制定上下级关系,仅此而已。真正开始的地方是在handleResumeActivity中通过windowManager的实现类的addView方法,方法内部通过ViewRootImpl的实例调用setView()方法。该方法内部的requestlayout()发出消息,开始执行doTraversal(),从而开始view三大流程。
补充,添加:7.LayoutInflater.inflate(layoutResID, mContentParent);修改:8.handleResumeActivity()。
(4)Window创建过程
(5)应用程序窗口(Activity)与WindowManagerService服务的连接
从两方面来看Activity组件与WindowManagerService服务之间的连接。一方面是从Activity到WindowManagerService服务的连接,另一方面是从WindowManagerService服务到Activity组件的连接。从Activity组件到WindowManagerService服务的连接是以Activity所在的应用程序进程为单位来进行的。当一个应用程序进程在启动第一个Activity组件的时候,它便会打开一个到WindowManagerService服务的连接,这个连接以应用程序进程从WindowManagerService服务处获得一个实现了IWindowSession接口的Session代理对象来标志。从WindowManagerService服务到Activity组件的连接是以Activity为单位来进行的。在应用程序进程这一侧,每一个Activity组件都关联一个实现了IWindow接口的W对象,这个W对象在Activity组件的视图对象创建完成之后,就会通过前面所获得一个Session代理对象来传递给WindowManagerService服务,而WindowManagerService服务接收到这个W对象之后,就会在内部创建一个WindowState对象来描述与该W对象所关联的Activity组件的窗口状态,并且以后就通过这个W对象来控制对应的Activity组件的窗口状态。
上述Activity组件与WindowManagerService服务之间的连接模型如图所示:
Session类和WindowState类的实现:(下节再详细介绍)
4 源码分析
4.1 ActivityThread.handleLaunchActivity
public final class ActivityThread {
......
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
// Initialize before creating the activity
WindowManagerGlobal.initialize();
//ActivityThread.performLaunchActivity
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
......
handleResumeActivity(r.token, false, r.isForward);
......
}
......
}
......
}
函数首先调用ActivityThread类的成员函数performLaunchActivity来创建要启动的Activity组件。在创建Activity组件的过程中,还会为该Activity组件创建窗口对象和视图对象。Activity组件创建完成之后,就可以将它激活起来了,这是通过调用ActivityThread类的成员函数handleResumeActivity来执行的。
4.2 ActivityThread.performLaunchActivity
public final class ActivityThread {
......
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
if (activity != null) {
......
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);//activity.attach
if (r.isPersistable()) {
//Instrumentation.callActivityOnCreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
......
}
......
}
......
}
4.3 Activity.onCreate
public class Instrumentation {
......
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
//Activity.performCreate()
activity.performCreate(icicle);
postPerformCreate(activity);
}
......
}
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
final void performCreate(Bundle icicle) {
restoreHasCurrentPermissionRequest(icicle);
//Activity.onCreate()
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
}
}
public class Hello extends Activity implements OnClickListener {
......
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//4.4 Activity.setContentView
setContentView(R.layout.main);
......
}
......
}
4.4 Activity.setContentView
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
public Window getWindow() {
return mWindow;
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent........
Window window) {
......
mWindow = new PhoneWindow(this, window);//PhoneWindow
mWindow.getLayoutInflater().setPrivateFactory(this);
mWindowManager = mWindow.getWindowManager();
......
}
......
public void setContentView(@LayoutRes int layoutResID) {
//4.5 PhoneWindow.setContentView(layoutResID)
getWindow().setContentView(layoutResID);
}
......
}
Activity类的成员函数setContentView首先调用另外一个成员函数getWindow来获得成员变量mWindow所描述的一个窗口对象,接着再调用这个窗口对象的成员函数setContentView来执行创建应用程序窗口视图对象的工作。
Activity类的成员变量mWindow指向的是一个PhoneWindow对象,因此接下来我们就继续分析PhoneWindow类的成员函数setContentView的实现。
4.5 PhoneWindow.setContentView(核心)
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private ViewGroup mContentParent;
......
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//4.6 installDecor(),获取到mContentParent视图
installDecor();
} else {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());
transitionTo(newScene);
} else {
//4.7 mLayoutInflater.inflate(layoutResID, mContentParent)
//将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent视图中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
......
}
mContentParent用来描述一个类型为DecorView的视图对象用作UI容器。当它的值等于null的时候,就说明正在处理的应用程序窗口的视图对象还没有创建。在这种情况下,就会调用函数installDecor来创建应用程序窗口视图对象。否则的话,就说明是要重新设置应用程序窗口的视图。在重新设置之前,首先调用变量mContentParent所描述的一个ViewGroup对象来移除原来的UI内空。
首先判断mContentParent是否为null,也就是第一次调运);如果是第一次调用,则调用installDecor()方法,否则判断是否设置FEATURE_CONTENT_TRANSITIONS Window属性(默认false),如果没有就移除该mContentParent内所有的所有子View;接着mLayoutInflater.inflate(layoutResID, mContentParent);将我们的资源文件通过LayoutInflater对象转换为View树,并且添加至mContentParent视图中。将参数layoutResID所描述的一个UI布局设置到前面所创建的应用程序窗口视图中去,最后还会调用一个Callback接口的函数onContentChanged来通知对应的Activity,它的视图内容发生改变了。
接下来,我们就继续分析函数installDecor的实现,以便可以继续了解应用程序窗口视图对象的创建过程。
4.6 PhoneWindow.installDecor
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private DecorView mDecor;
......
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
......
private TextView mTitleView;
......
private CharSequence mTitle = null;
......
private void installDecor() {
if (mDecor == null) {
//generateDecor()实现代码如下
mDecor = generateDecor();
......
}
if (mContentParent == null) {
//generateLayout(mDecor)实现代码如下
mContentParent = generateLayout(mDecor);
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
}
......
}
由于我们是在Activity启动中创建应用程序窗口视图的,因此假设此时mDecor的值等于null。这时候installDecor就会调用另外一个函数generateDecor来创建一个DecorView对象,并且保存在PhoneWindow类的成员变量mDecor中。generateDecor()代码如下:
protected DecorView generateDecor(int featureId) {
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
installDecor接着再调用另外一个函数generateLayout来根据当前应用程序窗口的Feature来加载对应的窗口布局文件。这些布局文件保存在frameworks/base/core/res/res/layout目录下,它们必须包含有一个id值为“content”的布局控件。这个布局控件必须要从ViewGroup类继承下来,用来作为窗口的UI容器。PhoneWindow类的成员函数generateLayout执行完成之后,就会这个id值为“content”的ViewGroup控件来给PhoneWindow类的成员函数installDecor,后者再将其保存在成员变量mContentParent中。generateLayout(decor)代码和DecorView添加至窗口的过程示例图如下:
protected ViewGroup generateLayout(DecorView decor) {
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else {
//系统提供的布局:R.layout.screen_simple
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//系统布局中content部分,加载自己的布局
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
mDecor.finishChanging();
return contentParent;
}
installDecor还会检查前面加载的窗口布局文件是否包含有一个id值为“title”的TextView控件。如果包含有的话,就会将它保存在PhoneWindow类的成员变量mTitleView中,用来描述当前应用程序窗口的标题栏。但是,如果当前应用程序窗口是没有标题栏的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么installDecor就需要将前面得到的标题栏隐藏起来。(注意,mTitleView所描述的标题栏有可能是包含在一个id值为“title_container”的容器里面的,在这种情况下,就需要隐藏该标题栏容器。另一方面,如果当前应用程序窗口是设置有标题栏的,那么installDecor就会设置它的标题栏文字。应用程序窗口的标题栏文字保存在变量mTitle中,我们可以调用函数setTitle来设置。)
这一步执行完成之后,应用程序窗口视图就创建完成了,回到前面的4.1中,即ActivityThread类的成员函数handleLaunchActivity中,接下来就会调用ActivityThread类的另外一个函数handleResumeActivity来激活正在启动的Activity组件。由于在是第一次激活该Activity组件,因此,在激活之前,还会为该Activity创建一个ViewRootImpl对象,并且与前面所创建的应用程序窗口视图关联起来,以便后面可以通过该ViewRootImpl对象来控制应用程序窗口视图的UI展现。
4.7 LayoutInflater.inflate(layoutResID, mContentParent);
4.7.1 得到LayoutInflater
LayoutInflater lif = LayoutInflater.from(Context context);
LayoutInflater lif = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//源码中LayoutInflater实例化获取的方法:
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
4.7.2 LayoutInflater源码的View inflate(…)方法族剖析
得到LayoutInflater对象之后我们就是传递xml然后解析得到View,如下方法:
public View inflate(int resource, ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//XmlResourceParser
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
获取到XmlResourceParser接口的实例(Android默认实现类为Pull解析XmlPullParser)。接着看第10行inflate(parser, root, attachToRoot);,你会发现无论哪个inflate重载方法最后都调运了inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)方法:
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
//定义返回值,初始化为传入的形参root
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
//如果一开始就是END_DOCUMENT,那说明xml文件有问题
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
//有了上面判断说明这里type一定是START_TAG,也就是xml文件里的root node
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
//处理merge tag的情况(merge,你懂的,APP的xml性能优化)
//root必须非空且attachToRoot为true,否则抛异常结束(APP使用merge时要注意的地方,
//因为merge的xml并不代表某个具体的view,只是将它包起来的其他xml的内容加到某个上层
//ViewGroup中。)
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//递归inflate方法调运
rInflate(parser, root, attrs, false, false);
} else {
// Temp is the root view that was found in the xml
//xml文件中的root view,根据tag节点创建view对象
final View temp = createViewFromTag(root, name, attrs, false);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
//根据root生成合适的LayoutParams实例
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//如果attachToRoot=false就调用view的setLayoutParams方法
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp
//递归inflate剩下的children
rInflate(parser, temp, attrs, true, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
//root非空且attachToRoot=true则将xml文件的root view加到形参提供的root里
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
//返回xml里解析的root view
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
//返回参数root或xml文件里的root view
return result;
}
}
接下来,我们就继续分析ActivityThread类的成员函数handleResumeActivity的实现。
4.8 ActivityThread.handleResumeActivity
public final class ActivityThread {
......
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
......
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
......
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) { //
r.window = r.activity.getWindow();
//4.9 getDecorView()
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//4.10 Activity.getWindowManager
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
......
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//4.11 (WindowManagerImpl)WindowManager.addView(decor, l)
wm.addView(decor, l);
}
}
......
}
......
}
......
}
函数handleResumeActivity首先调用另一个函数performResumeActivity来通知Activity,它要被激活了,即会导致Activity的函数onResume被调用。performResumeActivity的返回值是一个ActivityClientRecord对象r,这个ActivityClientRecord对象的变量activity描述的就是正在激活的Activity组件a。
handleResumeActivity接下来判断正在激活的Activity是否是可见的。如果可见,那么变量willBeVisible的值就会等于true。Activity类的变量mStartedActivity用来描述一个Activity是否正在启动一个新的Activity,并且等待这个新的Activity组件的执行结果。如果是的话,那么这个Activity组件的成员变量mStartedActivity的值就会等于true,表示在新的Activity组件的执行结果返回来之前,当前Activity组件要保持不可见的状态。因此,当Activity组件a的成员变量mStartedActivity的值等于true的时候,它接下来就是不可见的,否则的话,就是可见的。
虽然说在Activity组件a的变量mStartedActivity的值等于true的情况下,它接下来的状态要保持不可见的,但是有可能它所启动的Activity组件的UI不是全屏的。在这种情况下,Activity组件a的UI仍然是有部分可见的,这时候也要将变量willBeVisible的值设置为true。因此,如果前面得到变量willBeVisible的值等于false,那么函数handleResumeActivity接下来就会通过Binder进程间通信机制来调用ActivityManagerService服务的成员函数willActivityBeVisible来检查位于Activity组件a上面的其它Activity组件是否是全屏的。如果不是,那么ActivityManagerService服务的函数willActivityBeVisible的返回值就会等于true,表示接下来需要显示Activity组件a。
前面得到的ActivityClientRecord对象r的变量window用来描述当前正在激活的Activity组件a所关联的应用程序窗口对象。当它的值等于null的时候,就表示当前正在激活的Activity组件a所关联的应用程序窗口对象还没有关联一个ViewRootImpl对象。进一步地,如果这个正在激活的Activity组件a还活着,并且接下来是可见的,即ActivityClientRecord对象r的成员变量mFinished的值等于false,并且前面得到的变量willBeVisible的值等于true,那么这时候就说明需要为与Activity组件a所关联的一个应用程序窗口视图对象关联的一个ViewRootImpl对象。
将一个Activity的应用程序窗口视图对象与一个ViewRootImpl对象关联是通过该Activity组件所使用的窗口管理器来执行的。一个Activity组件所使用的本地窗口管理器保存它的成员变量mWindowManager中,这可以通过Activity类的函数getWindowManager来获得。在接下来的 中,我们再分析Activity类的成员函数getWindowManager的实现。
由于我们现在要给Activity组件a的应用程序窗口视图对象关联一个ViewRootImpl对象,因此,我们就需要首先获得这个应用程序窗口视图对象。从前面的Step 6可以知道,一个Activity组件的应用程序窗口视图对象保存在与其所关联的一个应用程序窗口对象的内部,因此,我们又要首先获得这个应用程序窗口对象。与一个Activity组件所关联的应用程序窗口对象可以通过调用该Activity组件的成员函数getWindow来获得。一旦获得了这个应用程序窗口对象(类型为PhoneWindow)之后,我们就可以调用它的成员函数getDecorView来获得它内部的视图对象。在接下来的Step 8和Step 9中,我们再分别分析Activity类的成员函数Activity类的成员函数getWindow和PhoneWindow类的成员函数getDecorView的实现。
在关联应用程序窗口视图对象和ViewRootImpl对象的时候,还需要第三个参数,即应用程序窗口的布局参数,这是一个类型为WindowManager.LayoutParams的对象,可以通过调用应用程序窗口的函数getAttributes来获得。还要判断最后一个条件是否成立,即当前正在激活的Activity组件a在本地进程中是否是可见的,即它的变量mVisibleFromClient的值是否等于true。如果是可见的,那么最后就可以调用前面所获得的一个本地窗口管理器wm(类型为LocalWindowManager)的成员函数addView来执行关联应用程序窗口视图对象和ViewRootImpl对象的操作。
接下来,我们就分别分析Activity类的成员函数getWindow、PhoneWindow类的成员函数getDecorView、ctivity类的成员函数getWindowManager以及LocalWindowManager类的成员函数addView的实现。
4.9 PhoneWindow.getDecorView
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private DecorView mDecor;
......
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor(); //installDecor();
}
return mDecor;
}
......
}
函数getDecorView首先判断变量mDecor的值是否等于null。如果是的话,那么就说明当前正在处理的应用程序窗口还没有创建视图对象。这时候就会调用另外一个函数installDecor来创建这个视图对象。从前面的调用过程可以知道,当前正在处理的应用程序窗口已经创建过视图对象,因此,这里的成员变量mDecor的值不等于null,函数getDecorView直接将它返回给调用者。
这一步执完成之后,返回到前面的4.9中,即函数handleResumeActivity中,接下来就会继续调用当前正在激活的Activity的函数getWindowManager来获得一个本地窗口管理器。
4.10 Activity.getWindowManager
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory, Window.Callback.... {
......
private Window mWindow;
private WindowManager mWindowManager;
public WindowManager getWindowManager() {
return mWindowManager;
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, ....
Window window) {
......
//mWindow.getWindowManager()
mWindowManager = mWindow.getWindowManager();
......
}
......
}
public abstract class Window {
private final WindowManager mWindowManager;
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
......
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//WindowManagerImpl
}
public WindowManager getWindowManager() {
return mWindowManager;
}
......
}
Activity类的成员变量mWindowManager指向的一是WindowManagerImpl的本地窗口管理器,Activity类的成员函数getWindowManager直接将它返回给调用者。
4.11 WindowManagerImpl.addView
public class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//WindowManagerGlobal.addView()
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
这一步执完成之后,返回到前面的4.9中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用前面所获得的WindowManagerImpl对象的函数addView来为当前正在激活的Activity组件的应用程序窗口视图对象关联一个ViewRootImpl对象。
4.12 WindowManagerGlobal.addView
public class WindowManagerGlobal {
......
private void addView(View view, ViewGroup.LayoutParams params, boolean nest) {
......
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImplImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
//4.13 ViewRootImpl.setView
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
WindowManagerGlobal类是如何关联一个应用程序窗口视图对象(View对象)和一个ViewRootImpl对象的? 一个View对象在与一个ViewRootImpl对象关联的同时,还会关联一个WindowManager.LayoutParams对象,这个WindowManager.LayoutParams对象是用来描述应用程序窗口视图的布局属性的。
WindowManagerGlobal类有三个成员变量mViews、mRoots和mParams,它们分别是类型为View、ViewRootImpl和WindowManager.LayoutParams的数组。这三个数组的大小是始终保持相等的。这样, 有关联关系的View对象、ViewRootImpl对象和WindowManager.LayoutParams对象就会分别保存在数组mViews、mRoots和mParams的相同位置上,也就是说,mViews[i]、mRoots[i]和mParams[i]所描述的View对象、ViewRootImpl对象和WindowManager.LayoutParams对象是具有关联关系的。因此,WindowManagerGlobal类的函数addView在关联一个View对象、一个ViewRootImpl对象和一个WindowManager.LayoutParams对象的时候,只要分别将它们放在数组mViews、mRoots和mParams的相同位置上就可以了。
参数view和参数params描述的就是要关联的View对象和WindowManager.LayoutParams对象。函数addView首先调用另外一个函数findViewLocked来检查参数view所描述的一个View对象是否已经存在于数组中mViews中了。如果已经存在的话,那么就说明该View对象已经关联过ViewRootImpl对象以及WindowManager.LayoutParams对象了。在这种情况下,如不允许重复对参数view所描述的一个View对象进行重新关联的。
如果参数view所描述的一个View对象还没有被关联过一个ViewRootImpl对象,那么成员函数addView就会创建一个ViewRootImpl对象,并且将它与参数view和params分别描述的一个View对象和一个WindowManager.LayoutParams对象保存在数组mViews、mRoots和mParams的相同位置上。注意,如果数组mViews、mRoots和mParams尚未创建,那么成员函数addView就会首先分别为它们创建一个大小为1的数组,以便可以用来分别保存所要关联的View对象、ViewRootImpl对象和WindowManager.LayoutParams对象。另一方面,如果数组mViews、mRoots和mParams已经创建,那么成员函数addView就需要分别将它们的大小增加1,以便可以在它们的末尾位置上分别保存所要关联的View对象、ViewRootImpl对象和WindowManager.LayoutParams对象。
还有另外一个需要注意的地方是当参数view描述的是一个子应用程序窗口的视图对象时,即WindowManager.LayoutParams对象wparams的成员变量type的值大于等于WindowManager.LayoutParams.FIRST_SUB_WINDOW并且小于等于WindowManager.LayoutParams.LAST_SUB_WINDOW时,那么成员函数addView还需要找到这个子视图对象的父视图对象panelParentView,这是通过遍历数组mRoots来查找的。首先,WindowManager.LayoutParams对象wparams的成员变量token指向了一个类型为W的Binder本地对象的一个IBinder接口,用来描述参数view所描述的一个子应用程序窗口视图对象所属的父应用程序窗口视图对象。其次,每一个ViewRootImpl对象都通过其成员变量mWindow来保存一个类型为W的Binder本地对象,因此,如果在数组mRoots中,存在一个ViewRootImpl对象,它的成员变量mWindow所描述的一个W对象的一个IBinder接口等于WindowManager.LayoutParams对象wparams的成员变量token所描述的一个IBinder接口时,那么就说明与该ViewRootImpl对象所关联的View对象即为参数view的父应用程序窗口视图对象。
成员函数addView为参数view所描述的一个View对象和参数params所描述的一个WindowManager.LayoutParams对象关联好一个ViewRootImpl对象root之后,最后还会将这个View对view象和这个WindowManager.LayoutParams对象,以及变量panelParentView所描述的一个父应用程序窗视图对象,保存在这个ViewRootImpl对象root的内部去,这是通过调用这个ViewRootImpl对象root的成员函数setView来实现的,因此,接下来我们就继续分析ViewRootImpl类的成员函数setView的实现。
4.15 ViewRootImpl.setView
public final class ViewRootImpl extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
......
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
......
View mView;
......
final View.AttachInfo mAttachInfo;
......
boolean mAdded;
......
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
......
mAttachInfo.mRootView = view;
.......
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
......
//requestLayout()
requestLayout();
......
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
......
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
......
}
......
}
}
......
}
参数view所描述的一个View对象会分别被保存在ViewRootImpl类的变量mView以及成员变量mAttachInfo所描述的一个AttachInfo的变量mRootView中,而参数attrs所描述的一个WindowManager.LayoutParams对象的内容会被拷贝到ViewRoot类的成员变量mWindowAttributes中去。
当参数panelParentView的值不等于null的时候,就表示参数view描述的是一个子应用程序窗口视图对象。在这种情况下,参数panelParentView描述的就是一个父应用程序窗口视图对象。这时候我们就需要获得用来描述这个父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,以便可以保存在ViewRoot类的成员变量mAttachInfo所描述的一个AttachInfo的成员变量 mPanelParentWindowToken中去。这样以后就可以知道ViewRoot类的成员变量mView所描述的一个子应用程序窗口视图所属的父应用程序窗口视图是什么了。注意,通过调用参数panelParentView的所描述的一个View对象的成员函数getApplicationWindowToken即可以获得一个对应的W对象的IBinder接口。
上述操作执行完成之后,ViewRoot类的成员函数setView就可以将成员变量mAdded的值设置为true了,表示当前正在处理的一个ViewRoot对象已经关联好一个View对象了。接下来,ViewRoot类的成员函数setView还需要执行两个操作:
- 调用ViewRoot类的另外一个函数requestLayout来请求对应用程序窗口视图的UI作第一次布局。
- 调用ViewRootImpl类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRoot所关联的一个应用程序窗口。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//scheduleTraversals()
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
//TraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//doTraversal()
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
//performTraversals(),整个View树的绘图流程在此开始
performTraversals();
}
}