• 布局文件是如何被解析的?


    原理

    通过layoutId,装换成xml对象,XmlResourceParser,将XmlResourceParser转成AttributeSet对象,根据XmlResourceParser的name得到view对象的类型,最后根据Constructor实例化view对象

    几个概念

    XmlResourceParser

    • 将xml转换成java对象,就是XmlResourceParser
    • Rescources class 中,根据resourceid,获得XmlResourceParser对象
    @NonNull
        XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
                throws NotFoundException {
            final TypedValue value = obtainTempTypedValue();
            try {
                final ResourcesImpl impl = mResourcesImpl;
                impl.getValue(id, value, true);
                if (value.type == TypedValue.TYPE_STRING) {
                    return impl.loadXmlResourceParser(value.string.toString(), id,
                            value.assetCookie, type);
                }
                throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
                        + " type #0x" + Integer.toHexString(value.type) + " is not valid");
            } finally {
                releaseTempTypedValue(value);
            }
        }
    

    AttributeSet

    • xml中标签对应的java对象,如layout_width等
    • 利用Xml class中的方法,生成AttributeSet对象
    public static AttributeSet asAttributeSet(XmlPullParser parser) {
            return (parser instanceof AttributeSet)
                    ? (AttributeSet) parser
                    : new XmlPullAttributes(parser);
        }
    

    Constructor

    • 属于反射的一种方式,通过constructor可以获取某个具体的类。 提供有关类的单个构造函数的访问和访问的信息。
    • 在xml解析成XmlPullParser时,将XmlPullParser解析成AttributeSet,获取AttributeSet的name,Constructor根据有包名的name,实例化一个view。
    final View view = constructor.newInstance(args);
    

    源码

    layoutInflater

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
            final Resources res = getContext().getResources();
            if (DEBUG) {
                Log.d(TAG, "INFLATING from resource: "" + res.getResourceName(resource) + "" ("
                        + Integer.toHexString(resource) + ")");
            }
    
            final XmlResourceParser parser = res.getLayout(resource);//画重点
            try {
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }
    
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                boolean ignoreThemeAttr) {
            if (name.equals("view")) {
                name = attrs.getAttributeValue(null, "class");
            }
    
            // Apply a theme wrapper, if allowed and one is specified.
            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();
            }
    
            if (name.equals(TAG_1995)) {
                // Let's party like it's 1995!
                return new BlinkLayout(context, attrs);
            }
    
            try {
                View view;
                //重点,通过设置Factory,可以达到将系统view替换成自定义view的目的
                if (mFactory2 != null) {
                    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 {//重点,如果是系统控件,则会在name前面加上"android.view."
                        if (-1 == name.indexOf('.')) {
                            view = onCreateView(parent, name, attrs);
                        } else {
                            view = createView(name, null, attrs);
                        }
                    } finally {
                        mConstructorArgs[0] = lastContext;
                    }
                }
    
                return view;
            }
            ...
        }
    
    public final View createView(String name, String prefix, AttributeSet attrs)
                throws ClassNotFoundException, InflateException {
            Constructor<? extends View> constructor = sConstructorMap.get(name);
            if (constructor != null && !verifyClassLoader(constructor)) {
                constructor = null;
                sConstructorMap.remove(name);
            }
            Class<? extends View> clazz = null;
    
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
    
                if (constructor == null) {
                    // Class not found in the cache, see if it's real, and try to add it
                    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 we have a filter, apply it to cached constructor
                    if (mFilter != null) {
                        // Have we seen this name before?
                        Boolean allowedState = mFilterMap.get(name);
                        if (allowedState == null) {
                            // New class -- remember whether it is allowed
                            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[] args = mConstructorArgs;
                args[1] = attrs;
    
                final View view = constructor.newInstance(args);//重点
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
    
            } 
            ...
        }
    

    Resources

    public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
            return loadXmlResourceParser(id, "layout");
        }
    
    @NonNull
        XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
                throws NotFoundException {
            final TypedValue value = obtainTempTypedValue();
            try {
                final ResourcesImpl impl = mResourcesImpl;
                impl.getValue(id, value, true);
                if (value.type == TypedValue.TYPE_STRING) {
                    return impl.loadXmlResourceParser(value.string.toString(), id,
                            value.assetCookie, type);//重点
                }
                throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
                        + " type #0x" + Integer.toHexString(value.type) + " is not valid");
            } finally {
                releaseTempTypedValue(value);
            }
        }
    
  • 相关阅读:
    linux如何查看ip地址
    mybais-plus整合springboot自动代码生成。
    org.springframework.beans.factory.UnsatisfiedDependencyException 问题
    springboot中使用AOP做访问请求日志
    springboot集成swagger
    springboot中的跨域问题
    spring中的ApplicationListener
    spring中的BeanDefinitionRegistryPostProcessor
    spring中的BeanFactoryPostProcessor
    servlet中ServletContainerInitializer
  • 原文地址:https://www.cnblogs.com/qinhe/p/6370781.html
Copyright © 2020-2023  润新知