Android view绘制流程详解(一)ViewTree的生成

ViewTree的生成

类的介绍:

  1. ActivityThread:它是Activity的一个主线程,是main入口。其中有个内部类ApplicationThread extends ApplicationNative extends Binder,当ApplicationNative通过binder通信在onTransact方法获取到Activity的各种生命周期,通过schedule_xxx_xxx_transaction的处理各个生命周期的分支,在分支中通过handler机制将消息发送到ActivityThread主线程的内部类H中。
  2. Instrumentation:这个类是一个Activity的中介类,ActivityThread通过这个类创建Activity。并且通过一系列的callActivityOnXXX方法去调用Activity的各种生命周期。
  3. Window类:是Android对系统所有窗口的一个抽象类,窗口的作用就是绘制UI,响应用户输入事件的一个区域。绘制独立,触发事件独立。(window和suerface的关系待补充)
  4. PhoneWindow:这个类是一个Window的一个实现类,它是Activity与view交互的中介类,DecorView就是在PhoneWindow中创建的,Activity通过getWindow()获取PhoneWindow对象,phoneWindow通过getCallback()方法获取Activity对象。
  5. DecorView:这个view是所有view的根,本质是一个FramLayout,其中包含actionBar和contentView,我们activity中setContentView就是将view添加到这里,id是android.R.id.content。这个view是测量布局绘制,以及事件分发的在view端的发起者。
  6. ViewRootImpl:这个类是在WindowManagerGlobal中创建,创建后通过setView方法,将DecorView传进来,并开始了view的绘制和分发事件,绘制通过perforTranversals()方法,分发通过WindowInputEventReveiver内部类和InputStage内部类,InputStage也是个责任链。所以ViewRootImpl也是很关键的一个类。
  7. Activity:触摸事件分发的起点(dispatchTouchEvent),各个生命周期的实现者。

绘制前的准备工作:

  1. ActivityThread接收到LAUNCH_ACTIVITY之后,创建activity对象,然后启动oncreate。
  2. 然后在setContentView中通过InflaterLayout.inflat方法将xml布局读取出来生成viewTree。
  3. 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分以下几种情况:

  1. 寻找布局根节点。
  2. 对merge标签单独判断处理,root!=null && attachToRoot==true继续解析,其他情况一律报错。
  3. 非merge标签,通过createViewFromTag方法创建tempView
  4. root!=null && attachToRoot==false,根据tempView属性创建LayoutParams,赋值给tempView并返回
  5. root!=null && attachToRoot==true,根据tempView属性创建LayoutParams,直接root.addView()
  6. 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);
    }

猜你喜欢

转载自blog.csdn.net/massonJ/article/details/107973063