Android的setContentView()方法咱们平时用不少,可是有多少人会点进setContentView()方法里面看看它的源码到底是何方神圣呢,今天我就来看看从这个方法里面究竟涉及到多少未知的知识。android
public class ViewActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view);
}
}
复制代码
怀着好奇心我点下了setContentView()这个方法去寻根索源:bash
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
复制代码
getWindow().setContentView(layoutResID)这是什么鬼,而后点进去看看:app
Windowide
摘自来自《Android开发艺术探索》的解释:布局
Window表示一个窗口的概念,在平常开发中直接接触Window的机会并很少,可是在某些特殊时候咱们须要在桌面上显示一个相似悬浮窗的东西,那么这种效果就须要用到Window来实现。Window是一个抽象类,他的具体实现是PhoneWindow。建立一个Window是很简单的事,只须要经过WindowManager便可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。Android全部的视图都是经过Window来呈现的,不论是Activity、Dialog仍是Toast,他们的视图实际上都是附加在Window上的,所以Window其实是View的直接管理者。post
IPC:Inter-Process Communication的缩写,含义为进程间通讯或者跨进程通讯,是指两个进程之间进行数据交换的过程。ui
看了一大轮的文字概念,我就想睡觉了。可是看了那么久,总算几个关键词PhoneWindow,WindowManager和WindowManagerService。上面讲到PhoneWindow是Window的实现类,那么咱们先去看看PhoneWindow吧。this
@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是什么?spa
ViewGroup mContentParent;
复制代码
暂时还不知道它是什么,那咱们当它是null吧,进入installDecor()方法看看:code
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);
复制代码
来到这里该总结一下了:
等等,虽然知道setContentView()方法是怎么来的,可是在看它的源码中,咱们还发现了好几个疑问:WindowManager,ViewRoot,ViewRootImpl,PhoneWindow,WindowManagerService。他们几个的关系又是怎么个错综复杂呢?拿着这些线索,咱们下一篇文章再来探个究竟吧。
个人掘金: juejin.im/user/594e8e…