原文出处——>[Android应用程序窗口(Activity)的视图对象(View)的创建过程分析](http://blog.csdn.net/luoshengyang/article/details/8245546)
从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。应用程序窗口视图是真正用来实现UI内容和布局的,也就是说,每一个Activity组件的UI内容和布局都是通过与其所关联的一个Window对象的内部的一个View对象来实现的。在本文中,我们就详细分析应用程序窗口视图的创建过程。
在前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中提到,应用程序窗口内部所包含的视图对象的实际类型为DecorView。DecorView类继承了View类,是作为容器(ViewGroup)来使用的,它的实现如图1所示:
:-: ![](https://box.kancloud.cn/f04dde05ea6e56c6f8d59f239a28ed6d_680x428.jpg)
图1 DecorView类的实现
这个图的具体描述可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中的图5,这里不再详述。
从前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文还可以知道,每一个应用程序窗口的视图对象都有一个关联的ViewRoot对象,这些关联关系是由窗口管理器来维护的,如图2所示:
:-: ![](https://box.kancloud.cn/5ccb934e8cd134a8b2ab2a3126da9e05_818x575.jpg)
图2 应用程序窗口视图与ViewRoot的关系图
这个图的具体描述可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中的图6,这里不再详述。
简单来说,ViewRoot相当于是MVC模型中的Controller,它有以下职责:
1. 负责为应用程序窗口视图创建Surface。
2. 配合WindowManagerService来管理系统的应用程序窗口。
3. 负责管理、布局和渲染应用程序窗口视图的UI。
那么,应用程序窗口的视图对象及其所关联的ViewRoot对象是什么时候开始创建的呢? 从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,Activity组件在启动的时候,系统会为它创建窗口对象(Window),同时,系统也会为这个窗口对象创建视图对象。另一方面,当Activity组件被激活的时候,系统如果发现与它的应用程序窗口视图对象所关联的ViewRoot对象还没有创建,那么就会先创建这个ViewRoot对象,以便接下来可以将它的UI渲染出来。
从前面Android应用程序启动过程源代码分析一文可以知道,Activity组件在启动的过程中,会调用ActivityThread类的成员函数handleLaunchActivity,用来创建以及首次激活Activity组件,因此,接下来我们就从这个函数开始,具体分析应用程序窗口的视图对象及其所关联的ViewRoot对象的创建过程,如图3所示:
![](https://box.kancloud.cn/5d9ffc061569cec9feeb80155f74054a_836x1105.jpg)
图3 应用程序窗口视图的创建过程
这个过程一共可以分为13个步骤,接下来我们就详细分析每一个步骤。
**Step 1. ActivityThread.handleLaunchActivity**
~~~
public final class ActivityThread {
......
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
......
handleResumeActivity(r.token, false, r.isForward);
......
}
......
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
函数首先调用ActivityThread类的成员函数performLaunchActivity来创建要启动的Activity组件。在创建Activity组件的过程中,还会为该Activity组件创建窗口对象和视图对象。Activity组件创建完成之后,就可以将它激活起来了,这是通过调用ActivityThread类的成员函数handleResumeActivity来执行的。
接下来,我们首先分析ActivityThread类的成员函数performLaunchActivity的实现,以便可以了解应用程序窗口视图对象的创建过程,接着再回过头来继续分析ActivityThread类的成员函数handleResumeActivity的实现,以便可以了解与应用程序窗口视图对象所关联的ViewRoot对象的创建过程。
**Step 2. ActivityThread.performLaunchActivity**
这个函数定义在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
这一步可以参考Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文的Step 1,它主要就是创建一个Activity组件实例,并且调用这个Activity组件实例的成员函数onCreate来让其执行一些自定义的初始化工作。
**Step 3. Activity.onCreate**
这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。
这一步可以参考Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文的Step 10。我们在实现一个Activity组件的时候,也就是在实现一个Activity子类的时候,一般都会重写成员函数onCreate,以便可以执行一些自定义的初始化工作,其中就包含初始化UI的工作。例如,在前面在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务一文中,我们实现了一个名称为Hello的Activity组件,用来测试硬件服务,它的成员函数onCreate的样子长得大概如下所示:
~~~
public class Hello extends Activity implements OnClickListener {
......
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
......
}
......
}
~~~
其中,调用从父类Activity继承下来的成员函数setContentView就是用来创建应用程序窗口视图对象的。
接下来,我们就继续分析Activity类的成员函数setContentView的实现。
**Step 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;
}
......
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。
Activity类的成员函数setContentView首先调用另外一个成员函数getWindow来获得成员变量mWindow所描述的一个窗口对象,接着再调用这个窗口对象的成员函数setContentView来执行创建应用程序窗口视图对象的工作。
从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,Activity类的成员变量mWindow指向的是一个PhoneWindow对象,因此,接下来我们就继续分析PhoneWindow类的成员函数setContentView的实现。
**Step 5. PhoneWindow.setContentView**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
// 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;
......
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
......
}
~~~
这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
PhoneWindow类的成员变量mContentParent用来描述一个类型为DecorView的视图对象,或者这个类型为DecorView的视图对象的一个子视图对象,用作UI容器。当它的值等于null的时候,就说明正在处理的应用程序窗口的视图对象还没有创建。在这种情况下,就会调用成员函数installDecor来创建应用程序窗口视图对象。否则的话,就说明是要重新设置应用程序窗口的视图。在重新设置之前,首先调用成员变量mContentParent所描述的一个ViewGroup对象来移除原来的UI内空。
由于我们是在Activity组件启动的过程中创建应用程序窗口视图的,因此,我们就假设此时PhoneWindow类的成员变量mContentParent的值等于null。接下来,函数就会调用成员函数installDecor来创建应用程序窗口视图对象,接着再通过调用PhoneWindow类的成员变量mLayoutInflater所描述的一个LayoutInflater对象的成员函数inflate来将参数layoutResID所描述的一个UI布局设置到前面所创建的应用程序窗口视图中去,最后还会调用一个Callback接口的成员函数onContentChanged来通知对应的Activity组件,它的视图内容发生改变了。从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,Activity组件自己实现了这个Callback接口,并且将这个Callback接口设置到了与它所关联的应用程序窗口对象的内部去,因此,前面实际调用的是Activity类的成员函数onContentChanged来发出一个视图内容变化通知。
接下来,我们就继续分析PhoneWindow类的成员函数installDecor的实现,以便可以继续了解应用程序窗口视图对象的创建过程。
**Step 6. PhoneWindow.installDecor**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
// This is the top-level view of the window, containing the window decor.
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) {
mDecor = generateDecor();
......
}
if (mContentParent == null) {
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);
}
}
}
}
......
}
~~~
这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
由于我们是在Activity组件启动的过程中创建应用程序窗口视图的,因此,我们同时假设此时PhoneWindow类的成员变量mDecor的值等于null。这时候PhoneWindow类的成员函数installDecor就会调用另外一个成员函数generateDecor来创建一个DecorView对象,并且保存在PhoneWindow类的成员变量mDecor中。
PhoneWindow类的成员函数installDecor接着再调用另外一个成员函数generateLayout来根据当前应用程序窗口的Feature来加载对应的窗口布局文件。这些布局文件保存在frameworks/base/core/res/res/layout目录下,它们必须包含有一个id值为“content”的布局控件。这个布局控件必须要从ViewGroup类继承下来,用来作为窗口的UI容器。PhoneWindow类的成员函数generateLayout执行完成之后,就会这个id值为“content”的ViewGroup控件来给PhoneWindow类的成员函数installDecor,后者再将其保存在成员变量mContentParent中。
PhoneWindow类的成员函数installDecor还会检查前面加载的窗口布局文件是否包含有一个id值为“title”的TextView控件。如果包含有的话,就会将它保存在PhoneWindow类的成员变量mTitleView中,用来描述当前应用程序窗口的标题栏。但是,如果当前应用程序窗口是没有标题栏的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么PhoneWindow类的成员函数installDecor就需要将前面得到的标题栏隐藏起来。注意,PhoneWindow类的成员变量mTitleView所描述的标题栏有可能是包含在一个id值为“title_container”的容器里面的,在这种情况下,就需要隐藏该标题栏容器。另一方面,如果当前应用程序窗口是设置有标题栏的,那么PhoneWindow类的成员函数installDecor就会设置它的标题栏文字。应用程序窗口的标题栏文字保存在PhoneWindow类的成员变量mTitle中,我们可以调用PhoneWindow类的成员函数setTitle来设置。
这一步执行完成之后,应用程序窗口视图就创建完成了,回到前面的Step 1中,即ActivityThread类的成员函数handleLaunchActivity中,接下来就会调用ActivityThread类的另外一个成员函数handleResumeActivity来激活正在启动的Activity组件。由于在是第一次激活该Activity组件,因此,在激活之前,还会为该Activity组件创建一个ViewRoot对象,并且与前面所创建的应用程序窗口视图关联起来,以便后面可以通过该ViewRoot对象来控制应用程序窗口视图的UI展现。
接下来,我们就继续分析ActivityThread类的成员函数handleResumeActivity的实现。
**Step 7. 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;
......
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
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();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
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;
wm.addView(decor, l);
}
}
......
}
......
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。
ActivityThread类的成员函数handleResumeActivity首先调用另外一个成员函数performResumeActivity来通知Activity组件,它要被激活了,即会导致Activity组件的成员函数onResume被调用。ActivityThread类的成员函数performResumeActivity的返回值是一个ActivityClientRecord对象r,这个ActivityClientRecord对象的成员变量activity描述的就是正在激活的Activity组件a。
ActivityThread类的成员函数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,那么ActivityThread类的成员函数handleResumeActivity接下来就会通过Binder进程间通信机制来调用ActivityManagerService服务的成员函数willActivityBeVisible来检查位于Activity组件a上面的其它Activity组件(包含了Activity组件a正在等待其执行结果的Activity组件)是否是全屏的。如果不是,那么ActivityManagerService服务的成员函数willActivityBeVisible的返回值就会等于true,表示接下来需要显示Activity组件a。
前面得到的ActivityClientRecord对象r的成员变量window用来描述当前正在激活的Activity组件a所关联的应用程序窗口对象。当它的值等于null的时候,就表示当前正在激活的Activity组件a所关联的应用程序窗口对象还没有关联一个ViewRoot对象。进一步地,如果这个正在激活的Activity组件a还活着,并且接下来是可见的,即ActivityClientRecord对象r的成员变量mFinished的值等于false,并且前面得到的变量willBeVisible的值等于true,那么这时候就说明需要为与Activity组件a所关联的一个应用程序窗口视图对象关联的一个ViewRoot对象。
将一个Activity组件的应用程序窗口视图对象与一个ViewRoot对象关联是通过该Activity组件所使用的窗口管理器来执行的。从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,一个Activity组件所使用的本地窗口管理器保存它的成员变量mWindowManager中,这可以通过Activity类的成员函数getWindowManager来获得。在接下来的Step 10中,我们再分析Activity类的成员函数getWindowManager的实现。
由于我们现在要给Activity组件a的应用程序窗口视图对象关联一个ViewRoot对象,因此,我们就需要首先获得这个应用程序窗口视图对象。从前面的Step 6可以知道,一个Activity组件的应用程序窗口视图对象保存在与其所关联的一个应用程序窗口对象的内部,因此,我们又要首先获得这个应用程序窗口对象。与一个Activity组件所关联的应用程序窗口对象可以通过调用该Activity组件的成员函数getWindow来获得。一旦获得了这个应用程序窗口对象(类型为PhoneWindow)之后,我们就可以调用它的成员函数getDecorView来获得它内部的视图对象。在接下来的Step 8和Step 9中,我们再分别分析Activity类的成员函数Activity类的成员函数getWindow和PhoneWindow类的成员函数getDecorView的实现。
在关联应用程序窗口视图对象和ViewRoot对象的时候,还需要第三个参数,即应用程序窗口的布局参数,这是一个类型为WindowManager.LayoutParams的对象,可以通过调用应用程序窗口的成员函数getAttributes来获得。一切准备就绪之后,还要判断最后一个条件是否成立,即当前正在激活的Activity组件a在本地进程中是否是可见的,即它的成员变量mVisibleFromClient的值是否等于true。如果是可见的,那么最后就可以调用前面所获得的一个本地窗口管理器wm(类型为LocalWindowManager)的成员函数addView来执行关联应用程序窗口视图对象和ViewRoot对象的操作。
接下来,我们就分别分析Activity类的成员函数getWindow、PhoneWindow类的成员函数getDecorView、ctivity类的成员函数getWindowManager以及LocalWindowManager类的成员函数addView的实现。
**Step 8. Activity.getWindow**
~~~
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
public Window getWindow() {
return mWindow;
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。
从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,Activity类的成员变量mWindow指向的是一个类型为PhoneWindow的窗口对象,因此,Activity类的成员函数getWindow返回给调用者的是一个PhoneWindow对象。
这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用前面所获得的一个PhoneWindow对象的成员函数getDecorView来获得当前正在激活的Activity组件所关联的一个应用程序窗口视图对象。
**Step 9. PhoneWindow.getDecorView**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
private DecorView mDecor;
......
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
......
}
~~~
这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
PhoneWindow类的成员函数getDecorView首先判断成员变量mDecor的值是否等于null。如果是的话,那么就说明当前正在处理的应用程序窗口还没有创建视图对象。这时候就会调用另外一个成员函数installDecor来创建这个视图对象。从前面的调用过程可以知道,当前正在处理的应用程序窗口已经创建过视图对象,因此,这里的成员变量mDecor的值不等于null,PhoneWindow类的成员函数getDecorView直接将它返回给调用者。
这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用当前正在激活的Activity组件的成员函数getWindowManager来获得一个本地窗口管理器。
**Step 10. Activity.getWindowManager**
~~~
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private WindowManager mWindowManager;
......
public WindowManager getWindowManager() {
return mWindowManager;
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。
从前面Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析一文可以知道,Activity类的成员变量mWindowManager指向的一是类型为LocalWindowManager的本地窗口管理器,Activity类的成员函数getWindowManager直接将它返回给调用者。
这一步执完成之后,返回到前面的Step 7中,即ActivityThread类的成员函数handleResumeActivity中,接下来就会继续调用前面所获得的一个LocalWindowManager对象的成员函数addView来为当前正在激活的Activity组件的应用程序窗口视图对象关联一个ViewRoot对象。
**Step 11. LocalWindowManager.addView**
~~~
public abstract class Window {
......
private class LocalWindowManager implements WindowManager {
......
public final void addView(View view, ViewGroup.LayoutParams params) {
......
mWindowManager.addView(view, params);
}
......
private final WindowManager mWindowManager;
......
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/view/Window.java中。
从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,LocalWindowManager类的成员变量mWindowManager指向的是一个WindowManagerImpl对象,因此,LocalWindowManager类的成员函数addView接下来调用WindowManagerImpl类的成员函数addView来给参数view所描述的一个应用程序窗口视图对象关联一个ViewRoot对象。
**Step 12. WindowManagerImpl.addView**
~~~
public class WindowManagerImpl implements WindowManager {
......
public void addView(View view, ViewGroup.LayoutParams params)
{
addView(view, params, false);
}
......
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
......
final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;
ViewRoot root;
View panelParentView = null;
synchronized (this) {
// Here's an odd/questionable case: if someone tries to add a
// view multiple times, then we simply bump up a nesting count
// and they need to remove the view the corresponding number of
// times to have it actually removed from the window manager.
// This is useful specifically for the notification manager,
// which can continually add/remove the same view as a
// notification gets updated.
int index = findViewLocked(view, false);
if (index >= 0) {
if (!nest) {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
root = mRoots[index];
root.mAddNesting++;
// Update layout parameters.
view.setLayoutParams(wparams);
root.setLayoutParams(wparams, true);
return;
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews != null ? mViews.length : 0;
for (int i=0; i<count; i++) {
if (mRoots[i].mWindow.asBinder() == wparams.token) {
panelParentView = mViews[i];
}
}
}
root = new ViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRoot[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}
......
private View[] mViews;
private ViewRoot[] mRoots;
private WindowManager.LayoutParams[] mParams;
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
在WindowManagerImpl类中,两个参数版本的成员函数addView是通过调用三个参数版本的成同函数addView来实现的,因此,我们接下来就主要分析三个参数版本的成员函数addView的实现。
在分析WindowManagerImpl类的三个参数版本的成员函数addView的实现之前,我们首先了解一下WindowManagerImpl类是如何关联一个应用程序窗口视图对象(View对象)和一个ViewRoot对象的。一个View对象在与一个ViewRoot对象关联的同时,还会关联一个WindowManager.LayoutParams对象,这个WindowManager.LayoutParams对象是用来描述应用程序窗口视图的布局属性的。
WindowManagerImpl类有三个成员变量mViews、mRoots和mParams,它们分别是类型为View、ViewRoot和WindowManager.LayoutParams的数组。这三个数组的大小是始终保持相等的。这样, 有关联关系的View对象、ViewRoot对象和WindowManager.LayoutParams对象就会分别保存在数组mViews、mRoots和mParams的相同位置上,也就是说,mViews[i]、mRoots[i]和mParams[i]所描述的View对象、ViewRoot对象和WindowManager.LayoutParams对象是具有关联关系的。因此,WindowManagerImpl类的三个参数版本的成员函数addView在关联一个View对象、一个ViewRoot对象和一个WindowManager.LayoutParams对象的时候,只要分别将它们放在数组mViews、mRoots和mParams的相同位置上就可以了。
理解了一个View对象、一个ViewRoot对象和一个WindowManager.LayoutParams对象是如何关联之后,WindowManagerImpl类的三个参数版本的成员函数addView的实现就容易理解了。
参数view和参数params描述的就是要关联的View对象和WindowManager.LayoutParams对象。成员函数addView首先调用另外一个成员函数findViewLocked来检查参数view所描述的一个View对象是否已经存在于数组中mViews中了。如果已经存在的话,那么就说明该View对象已经关联过ViewRoot对象以及WindowManager.LayoutParams对象了。在这种情况下,如果参数nest的值等于false,那么成员函数addView是不允许重复对参数view所描述的一个View对象进行重新关联的。另一方面,如果参数nest的值等于true,那么成员函数addView只是重新修改参数view所描述的一个View对象及其所关联的一个ViewRoot对象内部使用的一个WindowManager.LayoutParams对象,即更新为参数params所描述的一个WindowManager.LayoutParams对象,这是通过调用它们的成员函数setLayoutParams来实现的。
如果参数view所描述的一个View对象还没有被关联过一个ViewRoot对象,那么成员函数addView就会创建一个ViewRoot对象,并且将它与参数view和params分别描述的一个View对象和一个WindowManager.LayoutParams对象保存在数组mViews、mRoots和mParams的相同位置上。注意,如果数组mViews、mRoots和mParams尚未创建,那么成员函数addView就会首先分别为它们创建一个大小为1的数组,以便可以用来分别保存所要关联的View对象、ViewRoot对象和WindowManager.LayoutParams对象。另一方面,如果数组mViews、mRoots和mParams已经创建,那么成员函数addView就需要分别将它们的大小增加1,以便可以在它们的末尾位置上分别保存所要关联的View对象、ViewRoot对象和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所描述的一个子应用程序窗口视图对象所属的父应用程序窗口视图对象。其次,每一个ViewRoot对象都通过其成员变量mWindow来保存一个类型为W的Binder本地对象,因此,如果在数组mRoots中,存在一个ViewRoot对象,它的成员变量mWindow所描述的一个W对象的一个IBinder接口等于WindowManager.LayoutParams对象wparams的成员变量token所描述的一个IBinder接口时,那么就说明与该ViewRoot对象所关联的View对象即为参数view的父应用程序窗口视图对象。
成员函数addView为参数view所描述的一个View对象和参数params所描述的一个WindowManager.LayoutParams对象关联好一个ViewRoot对象root之后,最后还会将这个View对view象和这个WindowManager.LayoutParams对象,以及变量panelParentView所描述的一个父应用程序窗视图对象,保存在这个ViewRoot对象root的内部去,这是通过调用这个ViewRoot对象root的成员函数setView来实现的,因此,接下来我们就继续分析ViewRoot类的成员函数setView的实现。
**Step 13. ViewRoot.setView**
~~~
public final class ViewRoot 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();
......
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();
}
}
......
}
......
}
}
......
}
~~~
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。
参数view所描述的一个View对象会分别被保存在ViewRoot类的成员变量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还需要执行两个操作:
1. 调用ViewRoot类的另外一个成员函数requestLayout来请求对应用程序窗口视图的UI作第一次布局。
2. 调用ViewRoot类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRoot所关联的一个应用程序窗口。
至此,我们就分析完成Android应用程序窗口视图对象的创建过程了。在接下来的一篇文章中,我们将会继续分析Android应用程序窗口与WindowManagerService服务的连接过程,即Android应用程序窗口请求WindowManagerService为其增加一个WindowState对象的过程,而在接下来的两篇文章中,我们还会分析用来渲染Android应用程序窗口的Surface的创建过程,以及Android应用程序窗口的渲染过程。通过这三个过程的分析,我们就可以看到上述第1点和第2点提到的两个函数的执行过程,敬请期待!
- 前言
- Android组件设计思想
- Android源代码开发和调试环境搭建
- Android源代码下载和编译
- Android源代码情景分析法
- Android源代码调试分析法
- 手把手教你为手机编译ROM
- 在Ubuntu上下载、编译和安装Android最新源代码
- 在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)
- 如何单独编译Android源代码中的模块
- 在Ubuntu上为Android系统编写Linux内核驱动程序
- 在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
- 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务
- 在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务
- Android源代码仓库及其管理工具Repo分析
- Android编译系统简要介绍和学习计划
- Android编译系统环境初始化过程分析
- Android源代码编译命令m/mm/mmm/make分析
- Android系统镜像文件的打包过程分析
- 从CM刷机过程和原理分析Android系统结构
- Android系统架构概述
- Android系统整体架构
- android专用驱动
- Android硬件抽象层HAL
- Android应用程序组件
- Android应用程序框架
- Android用户界面架构
- Android虚拟机之Dalvik虚拟机
- Android硬件抽象层
- Android硬件抽象层(HAL)概要介绍和学习计划
- Android专用驱动
- Android Logger驱动系统
- Android日志系统驱动程序Logger源代码分析
- Android应用程序框架层和系统运行库层日志系统源代码分析
- Android日志系统Logcat源代码简要分析
- Android Binder驱动系统
- Android进程间通信(IPC)机制Binder简要介绍和学习计划
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
- Android Ashmem驱动系统
- Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划
- Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析
- Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析
- Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析
- Android应用程序进程管理
- Android应用程序进程启动过程的源代码分析
- Android系统进程Zygote启动过程的源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android应用程序消息机制
- Android应用程序消息处理机制(Looper、Handler)分析
- Android应用程序线程消息循环模型分析
- Android应用程序输入事件分发和处理机制
- Android应用程序键盘(Keyboard)消息处理机制分析
- Android应用程序UI架构
- Android系统的开机画面显示过程分析
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析
- SurfaceFlinger
- Android系统Surface机制的SurfaceFlinger服务
- SurfaceFlinger服务简要介绍和学习计划
- 启动过程分析
- 对帧缓冲区(Frame Buffer)的管理分析
- 线程模型分析
- 渲染应用程序UI的过程分析
- Android应用程序与SurfaceFlinger服务的关系
- 概述和学习计划
- 连接过程分析
- 共享UI元数据(SharedClient)的创建过程分析
- 创建Surface的过程分析
- 渲染Surface的过程分析
- Android应用程序窗口(Activity)
- 实现框架简要介绍和学习计划
- 运行上下文环境(Context)的创建过程分析
- 窗口对象(Window)的创建过程分析
- 视图对象(View)的创建过程分析
- 与WindowManagerService服务的连接过程分析
- 绘图表面(Surface)的创建过程分析
- 测量(Measure)、布局(Layout)和绘制(Draw)过程分析
- WindowManagerService
- WindowManagerService的简要介绍和学习计划
- 计算Activity窗口大小的过程分析
- 对窗口的组织方式分析
- 对输入法窗口(Input Method Window)的管理分析
- 对壁纸窗口(Wallpaper Window)的管理分析
- 计算窗口Z轴位置的过程分析
- 显示Activity组件的启动窗口(Starting Window)的过程分析
- 切换Activity窗口(App Transition)的过程分析
- 显示窗口动画的原理分析
- Android控件TextView的实现原理分析
- Android视图SurfaceView的实现原理分析
- Android应用程序UI硬件加速渲染
- 简要介绍和学习计划
- 环境初始化过程分析
- 预加载资源地图集服务(Asset Atlas Service)分析
- Display List构建过程分析
- Display List渲染过程分析
- 动画执行过程分析
- Android应用程序资源管理框架
- Android资源管理框架(Asset Manager)
- Asset Manager 简要介绍和学习计划
- 编译和打包过程分析
- Asset Manager的创建过程分析
- 查找过程分析
- Dalvik虚拟机和ART虚拟机
- Dalvik虚拟机
- Dalvik虚拟机简要介绍和学习计划
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的运行过程分析
- Dalvik虚拟机JNI方法的注册过程分析
- Dalvik虚拟机进程和线程的创建过程分析
- Dalvik虚拟机垃圾收集机制简要介绍和学习计划
- Dalvik虚拟机Java堆创建过程分析
- Dalvik虚拟机为新创建对象分配内存的过程分析
- Dalvik虚拟机垃圾收集(GC)过程分析
- ART虚拟机
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android运行时ART简要介绍和学习计划
- Android运行时ART加载OAT文件的过程分析
- Android运行时ART加载类和方法的过程分析
- Android运行时ART执行类方法的过程分析
- ART运行时垃圾收集机制简要介绍和学习计划
- ART运行时Java堆创建过程分析
- ART运行时为新创建对象分配内存的过程分析
- ART运行时垃圾收集(GC)过程分析
- ART运行时Compacting GC简要介绍和学习计划
- ART运行时Compacting GC堆创建过程分析
- ART运行时Compacting GC为新创建对象分配内存的过程分析
- ART运行时Semi-Space(SS)和Generational Semi-Space(GSS)GC执行过程分析
- ART运行时Mark-Compact( MC)GC执行过程分析
- ART运行时Foreground GC和Background GC切换过程分析
- Android安全机制
- SEAndroid安全机制简要介绍和学习计划
- SEAndroid安全机制框架分析
- SEAndroid安全机制中的文件安全上下文关联分析
- SEAndroid安全机制中的进程安全上下文关联分析
- SEAndroid安全机制对Android属性访问的保护分析
- SEAndroid安全机制对Binder IPC的保护分析
- 从NDK在非Root手机上的调试原理探讨Android的安全机制
- APK防反编译
- Android视频硬解稳定性问题探讨和处理
- Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
- Android应用程序安装过程源代码分析
- Android应用程序启动过程源代码分析
- 四大组件源代码分析
- Activity
- Android应用程序的Activity启动过程简要介绍和学习计划
- Android应用程序内部启动Activity过程(startActivity)的源代码分析
- 解开Android应用程序组件Activity的"singleTask"之谜
- Android应用程序在新的进程中启动新的Activity的方法和过程分析
- Service
- Android应用程序绑定服务(bindService)的过程源代码分析
- ContentProvider
- Android应用程序组件Content Provider简要介绍和学习计划
- Android应用程序组件Content Provider应用实例
- Android应用程序组件Content Provider的启动过程源代码分析
- Android应用程序组件Content Provider在应用程序之间共享数据的原理分析
- Android应用程序组件Content Provider的共享数据更新通知机制分析
- BroadcastReceiver
- Android系统中的广播(Broadcast)机制简要介绍和学习计划
- Android应用程序注册广播接收器(registerReceiver)的过程分析
- Android应用程序发送广播(sendBroadcast)的过程分析