每个activity对应的是同一个window对象吗?(不是)! 而是:每个Activity中都有一个相应的Window对象,控制界面布局文件的加载。每一个Window对象都对应着一个新的WindowManager对象(WindowManagerImpl实例)
http://blog.csdn.net/qq_23547831/article/details/51284556
1、当ActivityManagerService接收到启动Activity的请求之后会通过IApplicationThread进程间通讯告知ApplicationThread并执行handleLauncherActivity方法,handleLauncherActivity方法内部调用了performLaunchActivity方法,这个方法也是具体启动Activity的方法,该方法里是通过反射的机制创建的Activity,并调用了Activity的attach方法,那么这里的attach方法是做什么的呢?
在attach方法这里初始化了一些Activity的成员变量,主要是mWindow对象,并且mWindow的成员实例是PhoneWindow实例,这样也从侧面说明了一个Activity对应着一个Window对象( mWindow = new PhoneWindow(this))。除了window对象还初始化了一些Activity的其他成员变量,这里不再做讨论,继续回到我们的performLaunchActivity方法,在调用了Activity的attach方法之后又调用了:mInstrumentation.callActivityOnCreate(activity, r.state);这里的mInstrumentation是类Instrumentation,每个应用进程对应着一个Instrumentation和一个ActivityThread,Instrumentation就是具体操作Activity回调其生命周期方法的,我们这里看一下它的callActivityOnCreate方法的实现:
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);//再往里点击,该方法中调用了onCreate()方法。
postPerformCreate(activity);
}
至此我们就回调到了我们Activity的onCreate方法,大家平时在重写onCreate方法的时候,怎么加载布局文件的呢?
然后我们在看一下onCreate中的setContentView方法,这里的参数就是一个Layout布局文件,可以发现这里的setContentView方法就是Acitivty中的setContentView,好吧我们来看一下Activity中setContentView的实现:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
这里的getWindow方法就是获取Acitivty的mWindow成员变量,从刚刚我们在Activity.attach方法我们知道这里的mWindow的实例是PhoneWindow,所以这里调用的其实是PhoneWindow的setConentView方法,然后我们看一下PhoneWindow的setContentView是如何实现的。
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
这里的mContentParent对象是一个View对象,由于第一次mContentParent为空,所以执行installerDector方法,这里我们看一下installerDector方法的具体实现:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
...
}
这里的 mDector是一个DectorView对象,而DectorView继承与FrameLayout,所以这里的mDector其实就是一个FrameLayout对象 ,并通过调用generateDector()方法初始化,我们继续看一下generateDector方法的具体实现:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
好吧,就是通过new的方式创建了一个DectorView对象,然后我们继续看installDector方法:
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
这里初始化了mContentParent对象,这是一个View对象,我们调用了generateLayout方法,好吧,来看一下generateLayout方法的具体实现:
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;<strong><span style="color:#ff6666;">// 加载一个系统的布局文件(</span><span style="color:#000099;">根布局文件</span><span style="color:#ff6666;">)<span style="font-family: Arial, Helvetica, sans-serif;">R.layout.screen_simple</span></span></strong>
// System.out.println("Simple!");
}
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);<strong><span style="color:#ff0000;">//系统布局文件,初始化为mContentRoot</span></strong>.
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//在<span style="color: rgb(255, 0, 0); font-weight: bold; font-family: Arial, Helvetica, sans-serif;">mContentRoot上获得</span><span style="color: rgb(255, 0, 0); font-family: Arial, Helvetica, sans-serif; font-weight: bold;">contentParent .</span><span style="color: rgb(255, 0, 0); font-weight: bold; font-family: Arial, Helvetica, sans-serif;">
</span>
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks();
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
可以发现这里就是通过调用LayoutInflater.inflate方法来加载布局文件到内存中,关于LayoutInflater.inflater是如何加载布局文件的,并且,通过对代码的分析,我们发现PhoneWindow中的几个成员变量:mDector,mContentRoot,mContentParent的关系
mDector –> mContentRoot –> mContentParent(包含)
并且我们来看一下典型的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
这里就是整个Activity加载的跟布局文件:screen_simple.xml,其中ViewStub对应着Activity中的titleBar而这里的FrameLayout里面主要用于填充内容。
然后我们具体看一下LayoutInflater.inflater方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
这里调用了inflate的重载方法。。。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
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)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
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
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, 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.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception 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);
return result;
}
}
通过分析源码,不难发现,主要是通过循环解析xml文件并将信息解析到内存View对象,布局文件中定义的一个个组件都被顺序的解析到了内存中并被父子View的形式组织起来,这样通过给定的一个root View就可以将整个布局文件中定义的组件全部解析。分析完解析布局文件,回到我们的setContentVIew方法,在调用了installDector方法之后,又调用了:
mLayoutInflater.inflate(layoutResID, mContentParent);
这个方法的含义就是将我们传递的客户端的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的布局显示流程。