复制代码
通过上面我们知道最终调用的是PhoneWindow的setContentView,方法如下所示,主要干了两件事情,注释1用于生成DecorView和mContentParent;注释2处用于将layoutResID(也就是我们实现的布局id)设置到mContentParent中去;
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); //1
}
、、、
mLayoutInflater.inflate(layoutResID, mContentParent);//2
、、、
}
复制代码
installDecor方法代码如下所示,注释1处通过generateDecor方法生成mDecor变量,generateDecor方法内部其实就是new了一个DecorView;注释2处通过generateLayout方法获得mContentParent;mDecor是PhoneWindow的成员变量,是DecorView类型的,继承自FrameLayout;而这个mContentParent代表mDecor本身或者mDecor的子布局,什么意思呢?其实在mDecor的上部有一个占位View,根据不同的主题加载不同的DecorView,如果没有加载顶部view那mContentParent就是mDecor了;
private void installDecor() {
、、、
mDecor = generateDecor(-1); //1
、、、
mDecor.setWindow(this);
、、、
mContentParent = generateLayout(mDecor); //2
}
复制代码
经过上面的分析,我们发现Activity持有一个PhoneWindow的成员变量;PhoneWindow持有一个DecorView的成员变量,DecorView内部持有一个mContentParent,所以布局层次结构就是 Activity – PhoneWindow – DecorView – mContentParent;
而我们写的xml布局就是放在了mContentParent中;那么xml布局是如何加载到mContentParent容器的呢?其实也就是步骤2的注释2的mLayoutInflater.inflate(layoutResID, mContentParent)进行加载的,我们在下一部分分析加载的流程;
二、布局加载流程分析
1、mLayoutInflater.inflate(layoutResID, mContentParent)的调用链如下所示;
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null); //1
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot); //2
} finally {
parser.close();
}
}
复制代码
2、最终会调用到如下inflate方法,注释1处将root赋值给result;注释2处会通过createViewFromTag创建根View;注释4处用于inflate所有的children;这里解释一下最后一个attachToRoot参数,如果设置为true,会调用注释5将根view添加到root中然后将root返回;如果设置为false,会调用到注释3处,将root的参数设置到根view中去,但并不会执行addView操作,然后调用到注释6处将根view赋值给result,这样返回的就是根view而不是root;
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
View result = root; //1
final String name = parser.getName();
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs); //2
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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); //3
}
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true); //4
// 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); //5
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp; //6
}
return result;
}
复制代码
3、通过createViewFromTag创建根view,此方法调用链如下所示;注释1处是第一次创建view;注释2和注释3最终都会调用到注释3处的方法;注释1是一种情况,注释3是另一种情况,我们分别讨论一下;
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
View view = tryCreateView(parent, name, context, attrs); //1
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf(’.’)) {
view = onCreateView(context, parent, name, attrs); //2
} else {
view = createView(context, name, null, attrs); //3
}
}
}
return view;
}
复制代码
3-1 tryCreateView
第一种情况tryCreateView方法如下所示,如果mFactory2不为空,会通过注释1处mFactory2的onCreateView方法创建view;如果mFactory不为空,会通过注释2处mFactory的onCreateView创建view;那么这两个factory是什么呢?如注释3和注释4所示,Factory是一个单方法的接口,Factory2继承自Factory,重载了一个onCreateView方法;这两个变量初始都为空,因此我们开发者可以实现这两个接口,然后赋值给这两个变量,这样就会优先通过我们设置的方法加载view了;
public final
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
View tryCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs); //1
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs); //2
} else {
view = null;
}
return view;
}
private Factory mFactory; //3
private Factory2 mFactory2; //4
public interface Factory {
View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs);
}
public interface Factory2 extends Factory {
View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs);
}
复制代码
3-2 createView
因为系统并没有给mFactory和mFactory2赋值,因此会走到createView方法;方法代码如下所示,注释1处首先从sConstructorMap获取构造方法,第一次肯定为空;然后走到注释2处,通过反射获得构造方法,mConstructorSignature这个参数代表的是两个参数的构造方法,因此返回的就是带两个参数的构造方法;接着在注释3处将构造方法放入sConstructorMap中;最后在注释4处通过newInstance方法获取view实例并返回;
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name); //1
if (constructor == null) {
constructor = clazz.getConstructor(mConstructorSignature); //2
constructor.setAccessible(true);
sConstructorMap.put(name, constructor); //3
}
Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
Object[] args = mConstructorArgs;
args[1] = attrs;
final View view = constructor.newInstance(args); //4
return view;