android高级UI布局层次结构及布局加载流程源码解析,终局之战

复制代码

2、mWindow.setContentView(layoutResID)

通过上面我们知道最终调用的是PhoneWindow的setContentView,方法如下所示,主要干了两件事情,注释1用于生成DecorView和mContentParent;注释2处用于将layoutResID(也就是我们实现的布局id)设置到mContentParent中去;

public void setContentView(int layoutResID) {

if (mContentParent == null) {

installDecor(); //1

}

、、、

mLayoutInflater.inflate(layoutResID, mContentParent);//2

、、、

}

复制代码

3、installDecor()

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

}

复制代码

4、布局层次

经过上面的分析,我们发现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;

上一篇:Linux Preload Hook原理与实践


下一篇:Android系统自带样式(android:theme)