ViewTree的生成
类的介绍:
- ActivityThread:它是Activity的一个主线程,是main入口。其中有个内部类ApplicationThread extends ApplicationNative extends Binder,当ApplicationNative通过binder通信在onTransact方法获取到Activity的各种生命周期,通过schedule_xxx_xxx_transaction的处理各个生命周期的分支,在分支中通过handler机制将消息发送到ActivityThread主线程的内部类H中。
- Instrumentation:这个类是一个Activity的中介类,ActivityThread通过这个类创建Activity。并且通过一系列的callActivityOnXXX方法去调用Activity的各种生命周期。
- Window类:是Android对系统所有窗口的一个抽象类,窗口的作用就是绘制UI,响应用户输入事件的一个区域。绘制独立,触发事件独立。(window和suerface的关系待补充)
- PhoneWindow:这个类是一个Window的一个实现类,它是Activity与view交互的中介类,DecorView就是在PhoneWindow中创建的,Activity通过getWindow()获取PhoneWindow对象,phoneWindow通过getCallback()方法获取Activity对象。
- DecorView:这个view是所有view的根,本质是一个FramLayout,其中包含actionBar和contentView,我们activity中setContentView就是将view添加到这里,id是android.R.id.content。这个view是测量布局绘制,以及事件分发的在view端的发起者。
- ViewRootImpl:这个类是在WindowManagerGlobal中创建,创建后通过setView方法,将DecorView传进来,并开始了view的绘制和分发事件,绘制通过perforTranversals()方法,分发通过WindowInputEventReveiver内部类和InputStage内部类,InputStage也是个责任链。所以ViewRootImpl也是很关键的一个类。
- Activity:触摸事件分发的起点(dispatchTouchEvent),各个生命周期的实现者。
绘制前的准备工作:
- ActivityThread接收到LAUNCH_ACTIVITY之后,创建activity对象,然后启动oncreate。
- 然后在setContentView中通过InflaterLayout.inflat方法将xml布局读取出来生成viewTree。
- ActivityThread接收到RESUME_ACTIVITY之后,通过执行调度遍历,开启了view的绘制。
当ActivityThread的H接收到LAUNCH_ACTIVITY消息的时候,调用了
case LAUNCH_ACTIVITY: {
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
然后通过performLaunchActivity创建一个Activity对象
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
Activity a = performLaunchActivity(r, customIntent);关键看这句
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
..............
Activity activity = null;
通过Instrumentation工具类,创建一个Activity对象。
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
try {
创建一个Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
................
调用这个activity的attach方法,然后在activity的attach方法中new了一个PhoneWindow
activity.attach(传入一堆参数);
..........
通过callActivityOnCreate()方法调用Activity的onCreate方法。然后调用setContentView。
mInstrumentation.callActivityOnCreate(......);
............
return activity;
}
View的生成
setContentView最后还是调用的LayoutInflater.inflat()方法将xml文件解析成treeView
public void setContentView(int resId) {
ensureSubDecor();
首先获取content根view,然后通过inflate方法将自定义布局添加到根布局中。
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
这个预编译是从Android-29开始添加的,也是通过反射快读预编译view,成功后直接返回,提高效率
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {return view;}
首先获取xml布局解析器
final XmlResourceParser parser = res.getLayout(resource);
try {
遍历解析xml布局
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}}
遍历解析xml布局根据root和attachToRoot分以下几种情况:
- 寻找布局根节点。
- 对merge标签单独判断处理,root!=null && attachToRoot==true继续解析,其他情况一律报错。
- 非merge标签,通过createViewFromTag方法创建tempView
- root!=null && attachToRoot==false,根据tempView属性创建LayoutParams,赋值给tempView并返回
- root!=null && attachToRoot==true,根据tempView属性创建LayoutParams,直接root.addView()
- rootnull || attachToRootfalse,返回tempView,且没有layoutParams属性;
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;
try {
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();
找到根节点后,单独处理marge标签。如果是Merge标签,则必须依附于一个RootView,否则抛出异常
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, attrs, false, false);
} else {
不是marge标签的,根据节点名来创建View对象
final View temp = createViewFromTag(root, name, attrs, false);
ViewGroup.LayoutParams params = null;
if (root != null) {
如果设置的Root不为null,则根据当前标签的参数生成LayoutParams
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
如果不是attachToRoot ,则对这个Tag和创建出来的View设置LayoutParams;注意:此处的params只有当被添加到一个View中的时候才会生效;
temp.setLayoutParams(params);
}
}
rInflate(parser, temp, attrs, true, true);
if (root != null && attachToRoot) {
如果Root不为null且是attachToRoot,将创建出来的tampView添加到root中,并返回root。
root.addView(temp, params);
}
Root为null或者false的情况就只返回创建的tempView,并且没有layoutParams属性。
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
}
return result;
}
}
根据父布局,节点名字,和属性,调用createViewFromTag创建view
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
这里有个小插曲,这是为了纪念1995年而产生的blinkLayout,传说中blingbling的会闪
if (name.equals(TAG_1995)) {
return new BlinkLayout(context, attrs);
}
try {
创建View的核心工作就是通过一下几个onCreateView方法来完成的
View view;
if (mFactory2 != null) {
//这个factory2目前可以用来实现一键换肤的功能
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
//带点的是自定义view,这就是为什么自定义view要用全类名,否则就当系统view处理报空指针
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
//不带点的表示系统view,直接走createView
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
}
}
onCreateView方法最后调用的是createView方法,主要通过反射获取到自定义的View:
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
//先从缓存中获取自定义View的构造,有的话直接反射获取类
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
if (constructor == null) {
通过全类名反射获取clazz对象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
构造没有缓存,直接通过反射调用构造函数,并缓存起来
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
if (mFilter != null) {
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
Object lastContext = mConstructorArgs[0];
if (mConstructorArgs[0] == null) {
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
通过构造函数获取view实例,这就算是创建完了,如果是viewStub,会设置LayoutInflate,
只用的时候在通过inflate加载ViewStub中的布局然后添加到父View中,这就是为什么ViewStub
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
} catch (ClassCastException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
}catch (Exception e) {
InflateException ie = new InflateException(
attrs.getPositionDescription() + ": Error inflating class "
+ (clazz == null ? "<unknown>" : clazz.getName()), e);
}
}
然后深入遍历,在上面的inflate()方法中,最终也都是通过rInflate()方法去深入遍历子view并加载
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
同理,寻找根节点,,找到后获取name,然后对focus、tag、include、meger等标签的处理
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
//如果该节点为requestFocus标签
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
//如果该节点为tag标签
parseViewTag(parser, parent, attrs); 这个就是获取tag标签并设置
} else if (TAG_INCLUDE.equals(name)) {
//如果该节点为include标签
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
//如果该节点为merge标签
throw new InflateException("<merge /> must be the root element");
} else {
//如果该标签是正常的标签
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//继续深入遍历子view
rInflateChildren(parser, view, attrs, true);
//最后将得到的viewTree添加到viewgroup中去
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
Include标签处理中对include进行了处理,过程几乎和inflate一样:
private void parseInclude(XmlPullParser parser, Context context, View parent,
AttributeSet attrs) throws XmlPullParserException, IOException {
int type;
if (parent instanceof ViewGroup) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
final boolean hasThemeOverride = themeResId != 0;
if (hasThemeOverride) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
...........获取include中的layout属性............
if (layout == 0) {
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
throw new InflateException("You must specify a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
} else {
找到layout之后,寻找start节点,然后获取名字,然后处理merger以及非merge,步骤跟inflate几乎一样,
区别就是这里不对root以及attachToRoot做判断,并对include的属性id和visible做判断。
final XmlResourceParser childParser = context.getResources().getLayout(layout);
try {
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty.
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(childParser.getPositionDescription() +
": No start tag found!");
}
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) {
//同样的,如果是merge标签直接深入便利
rInflate(childParser, parent, context, childAttrs, false);
} else {
//如果不是merge标签,则先获取当前跟节点View
final View view = createViewFromTag(parent, childName,
context, childAttrs, hasThemeOverride);
final ViewGroup group = (ViewGroup) parent;
//然后获取根节点的属性值,生成一个params对象
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.Include);
final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
final int visibility = a.getInt(R.styleable.Include_visibility, -1);
a.recycle();
ViewGroup.LayoutParams params = null;
try {
params = group.generateLayoutParams(attrs);
} catch (RuntimeException e) {
}
if (params == null) {
params = group.generateLayoutParams(childAttrs);
}
//将属性值封装类params设置到view中
view.setLayoutParams(params);
//然后继续深入便利
rInflateChildren(childParser, view, childAttrs, true);
if (id != View.NO_ID) {
view.setId(id);
}
switch (visibility) {
case 0:
view.setVisibility(View.VISIBLE);
break;
case 1:
view.setVisibility(View.INVISIBLE);
break;
case 2:
view.setVisibility(View.GONE);
break;
}
//把最终得到的ViewTree添加到group中去
group.addView(view);
}
} finally {
childParser.close();
}
}
} else {
throw new InflateException("<include /> can only be used inside of a ViewGroup");
}
最有都是一样,调用consumeChildElements方法
LayoutInflater.consumeChildElements(parser);
}
在上面的rInflate()方法中,最终还是会调用rInflateChildren()来深入遍历子view,其实就是递归调用了rInflate方法遍历子view。
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}