Android的setContentView()方法我们平时用很多,但是有多少人会点进setContentView()方法里面看看它的源码究竟是何方神圣呢,今天我就来看看从这个方法里面究竟涉及到多少未知的知识。
public class ViewActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view);
}
}
怀着好奇心我点下了setContentView()这个方法去寻根索源:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow().setContentView(layoutResID)这是什么鬼,然后点进去看看:
image
我的天,竟然看到setContentView()是一个叫Window类的抽象方法,Window相信每个人都听过,但是对于Android的Window相信不是所有人都了解,我也是一样,然后我带着问题翻阅了书本。
Window
摘自来自《Android开发艺术探索》的解释:
Window表示一个窗口的概念,在日常开发中直接接触Window的机会并不多,但是在某些特殊时候我们需要在桌面上显示一个类似悬浮窗的东西,那么这种效果就需要用到Window来实现。Window是一个抽象类,他的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。Android所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,他们的视图实际上都是附加在Window上的,因此Window实际上是View的直接管理者。
IPC:Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。
看了一大轮的文字概念,我就想睡觉了。但是看了那么久,总算几个关键词PhoneWindow,WindowManager和WindowManagerService。上面讲到PhoneWindow是Window的实现类,那么我们先去看看PhoneWindow吧。
@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();
}
...
}
在PhoneWindow确实找到了setContentView()方法的具体实现。
mContentParent是什么?
ViewGroup mContentParent;
暂时还不知道它是什么,那我们当它是null吧,进入installDecor()方法看看:
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}
...
下面省略了一大堆UiOptions,setIcon,Transition的方法
} else {
...
}
}
mDecor是什么?
private DecorView mDecor;
DecorView是什么?
书本是这样写的:
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程(onMeasure(),onLayout(),onDraw())均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
我真的醉了,越翻越多自己不懂的概念出来:ViewRoot,ViewRootImpl,现在暂且做个笔记吧,先不管了。先看看我们找到的线索:
当Activity对象被创建完毕后,会将DecorView添加到Window中.
这就是我们要找的东西。DecorView原来是这样用的。
回到installDecor()中,当mDecor为空时,调用generateDecor(-1)方法:
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
这里创建了一个DecorView了,我们发现DecorView其实是一个FrameLayout,再回到installDecor(),这时候我们知道mContentParent仍然为null,那么进入generateLayout(mDecor)方法:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
//根据当前设置的主题来加载默认布局
TypedArray a = getWindowStyle();
...
设置各种各样的属性
...
//如果你在theme中设置了window_windowNoTitle,则这里会调用到,其他方法同理,
//这里是根据你在theme中的设置去设置的
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
requestFeature(FEATURE_ACTION_BAR);
}
//是否有设置全屏
if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
...
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 {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//选择对应布局创建添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
首先generateLayout会根据当前用户设置的主题去设置对应的Feature,接着,根据对应的Feature来选择加载对应的布局文件,(Window.FEATURE_NO_TITLE)接下来通过getLocalFeatures来获取你设置的feature,进而选择加载对应的布局,这也就是为什么我们要在setContentView之前调用requesetFeature的原因。
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
我们还能看到contentParent其实是一个叫com.android.internal.R.id.content的布局,最后添加到DecorView上。generateLayout()方法最后返回的就是contentParent。好了,installDecor()方法走完了,创建了DecorView和在上面设置了一大堆属性,并创建带来了一个mContentParent,再回到PhoneWindow的setContentView()中
@Override
public void setContentView(int layoutResID) {
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 {
//把mContentParent加载到mLayoutInflater中,
//而mLayoutInflater在上面generateLayout(DecorView decor)方法中
//早已加载到DecorView中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
//回调通知表示完成界面改变
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
此时已经创建完DecorView并且获取到mContentParent,接着就是将你setContentView的内容添加到mContentParent中,也就是
mLayoutInflater.inflate(layoutResID, mContentParent);
或者
mContentParent.addView(view, params);
来到这里该总结一下了:
image
真是千言万语都在这图中了,网上盗的图真是万能的,我的总结都在这个图中了。
我以前一直不懂为什么setContentView()叫setContentView而不叫setView呢,那是因为我们所创建的布局其实是Activity里面的PhoneWindow创建出来的DecorView里面的ContentView来的而已。一开始以为你是老大,现在才发现你是个小弟大概就是这种感觉吧。
等等,虽然知道setContentView()方法是怎么来的,但是在看它的源码中,我们还发现了好几个疑问:WindowManager,ViewRoot,ViewRootImpl,PhoneWindow,WindowManagerService。他们几个的关系又是怎么个错综复杂呢?拿着这些线索,我们下一篇文章再来探个究竟吧。