Android LayoutInflater 源码解析

来源:互联网 发布:linux 主机管理系统 编辑:程序博客网 时间:2024/06/05 08:43
好记性不如烂笔头,读过很快就会忘记,但是记下来忘记的时候还可以来翻阅

开始正文

这个工具用来解析Android中的XML布局,把XML布局文件转换成Java对象进行操作。
简单介绍下这个类的使用方法,来引刨析过程!

//调用inflate方法就可以把XML解析成View对象View contentView = LayoutInflater.from(this).inflate(R.layout.activity_main, null);


//判断是否需要把resource添加到root容器中,然后继续向下调用,这个root参数//就暂且称之为 装饰view 吧public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {        return inflate(resource, root, root != null);}

//获取xml解析器,然后继续向下传递public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {        final Resources res = getContext().getResources();        final XmlResourceParser parser = res.getLayout(resource);        try {            return inflate(parser, root, attachToRoot);        } finally {            parser.close();        }    }

//这是整个的inflate的代码,下面逐步去解析下边的复杂调用public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {        synchronized (mConstructorArgs) {            final Context inflaterContext = mContext;            final AttributeSet attrs = Xml.asAttributeSet(parser);            Context lastContext = (Context) mConstructorArgs[0];            mConstructorArgs[0] = inflaterContext;            View result = root;            try {                //标准的pull解析,定位开始标记                int type;                while ((type = parser.next()) != XmlPullParser.START_TAG &&                        type != XmlPullParser.END_DOCUMENT) {                    // Empty                }                //这里是一个简单的容错判断,仅仅是验证xml是否合法                if (type != XmlPullParser.START_TAG) {                    throw new InflateException(parser.getPositionDescription()                            + ": No start tag found!");                }                //获取xml布局的根标签的名字                final String name = parser.getName();                //判断标签是否是<merge>,如果是merge就用rInflate去解析                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");                    }                    //回在最后讲,最后会调用他去创建view tree                    rInflate(parser, root, inflaterContext, attrs, false);                } else {                    //创建View树的跟View                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);                    ViewGroup.LayoutParams params = null;                                      if (root != null) {                        // 获取container中的LayoutParams参数                        params = root.generateLayoutParams(attrs);                        if (!attachToRoot) {                            //给根View设置默认的布局参数                            //如果root传递null,那么就会按照wrap_content的方式去加载                            temp.setLayoutParams(params);                        }                    }                    //解析xml剩下的所有的布局,并在根View中创建View树                    rInflateChildren(parser, temp, attrs, true);                                        //如果需要装饰就直接调用addView方法,把刚刚生成的 View树 添加到root容器中                    if (root != null && attachToRoot) {                        root.addView(temp, params);                    }                    //不需要装饰就直接return                    if (root == null || !attachToRoot) {                        result = temp;                    }                }            } catch (XmlPullParserException e) {                InflateException ex = new InflateException(e.getMessage());                ex.initCause(e);                throw ex;            } catch (Exception e) {                InflateException ex = new InflateException(                        parser.getPositionDescription()                                + ": " + e.getMessage());                ex.initCause(e);                throw ex;            } finally {                // Don't retain static reference on context.                mConstructorArgs[0] = lastContext;                mConstructorArgs[1] = null;            }            return result;        }    }
解析View的大体流程就这么简单,解析xml,根据标签生成view结构树,最后返回

下面来看一下createViewFromTag函数,看看是如何创建根View的
//这个很简单,封装一下,继续向下调用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) {        //判断传进来的标签的名字        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就是blink,具体做什么的我也沒接触过        if (name.equals(TAG_1995)) {            return new BlinkLayout(context, attrs);        }        try {            View 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;            }            //同样也是询问是否用自定义的工去创建View tree            if (view == null && mPrivateFactory != null) {                view = mPrivateFactory.onCreateView(parent, name, context, attrs);            }            //如果没有set过任何factory,那么就是默认的创建方式            if (view == null) {                final Object lastContext = mConstructorArgs[0];                mConstructorArgs[0] = context;                try {                    //这块是自定义控件还是原生控件,原生控件都有包名,所以用查找.去判断                    //然后调用onCreateView去创建View tree                    //这个是一个抽象方法,具体实现在PhoneLayoutInflater类中                    //然后返回具体的View对象                    if (-1 == name.indexOf('.')) {                        view = onCreateView(parent, name, attrs);                    } else {                        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);            ie.initCause(e);            throw ie;        } catch (Exception e) {            final InflateException ie = new InflateException(attrs.getPositionDescription()                    + ": Error inflating class " + name);            ie.initCause(e);            throw ie;        }    }

这里贴出LayoutInflater创建实例的代码,就可以清楚的了解实现类了
//这段代码在SystemServiceRegistry中,把服务注册到系统中,可以看到是PhoneLayoutInflaterregisterService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,                new CachedServiceFetcher<LayoutInflater>() {            @Override            public LayoutInflater createService(ContextImpl ctx) {                return new PhoneLayoutInflater(ctx.getOuterContext());}});

接下来我在贴出PhoneLayoutInflater中的onCreateView方法,也很简单

 @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {    private static final String[] sClassPrefixList = {        "android.widget.",        "android.webkit.",        "android.app."    };     for (String prefix : sClassPrefixList) {         try {     //这块又调用他父类的方法去创建View             View view = createView(name, prefix, attrs);             if (view != null) {                 return view;             }         } catch (ClassNotFoundException e) {             //....         }     }     //如果前几个包都没有满足条件的,还有最后一手,super     return super.onCreateView(name, attrs);    }
//我们可以看到,这个super的方法,是直接定位android.view包的protected View onCreateView(String name, AttributeSet attrs)            throws ClassNotFoundException {        return createView(name, "android.view.", attrs);}

public final View createView(String name, String prefix, AttributeSet attrs)            throws ClassNotFoundException, InflateException {         //先判断是否已经存在实例        Constructor<? extends View> constructor = sConstructorMap.get(name);        Class<? extends View> clazz = null;        try {            if (constructor == null) {                //这块在for循环的帮助下,去查找类,并加载                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[] args = mConstructorArgs;            args[1] = attrs;//这是一个context对象            //创建一个View实例            final View view = constructor.newInstance(args);            //这快判断一下是否是View存根,存根就初始化设置一下加载器            if (view instanceof ViewStub) {                final ViewStub viewStub = (ViewStub) view;                viewStub.setLayoutInflater(cloneInContext((Context) args[0]));            }            //到这快就真正执行完createViewFromTag方法了,返回根View            return view;        } catch (NoSuchMethodException e) {            //....        } catch (ClassCastException e) {            //....        } catch (ClassNotFoundException e) {            //....        } catch (Exception e) {            //....        } finally {            //....        }    }
到这快创建根View就分析完了,接下来就是给根View构建View tree了
构建关系是在rInflateChildren这个方法中进行,我们去分析一下

//调用一下rInflate方法,我贴出rInflate的源码final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,            boolean finishInflate) throws XmlPullParserException, IOException {        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);    }

void rInflate(XmlPullParser parser, View parent, Context context,            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {        final int depth = parser.getDepth();        int type;        while (((type = parser.next()) != XmlPullParser.END_TAG ||                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {            if (type != XmlPullParser.START_TAG) {                continue;            }            //获取xml标签名字            final String name = parser.getName();            if (TAG_REQUEST_FOCUS.equals(name)) {                //调用parent的view.requestFocus方法注册焦点                parseRequestFocus(parser, parent);            } else if (TAG_TAG.equals(name)) {                //给parent设置tag,view.setTag方法                parseViewTag(parser, parent, attrs);            } else if (TAG_INCLUDE.equals(name)) {                if (parser.getDepth() == 0) {                    throw new InflateException("<include /> cannot be the root element");                }                //解析include标签,大概原理就是找到layout                //用上边的rInflate createViewFromTag和rInflateChildren进行view树创建                //创建好后调用parent的addView加进去                parseInclude(parser, context, parent, attrs);            } else if (TAG_MERGE.equals(name)) {                throw new InflateException("<merge /> must be the root element");            } else {                //最后到控件部分,各种TextView,Button,ImageView啊之类的                //还是那个套路,通过createViewFromTag创建View对象                //这块是个递归思路                final View view = createViewFromTag(parent, name, context, attrs);                //获取当前标签的父View                final ViewGroup viewGroup = (ViewGroup) parent;                //获取布局参数                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);                //继续递归,直到最底层View,然后逐步向上创建,并加到viewGroup容器中                rInflateChildren(parser, view, attrs, true);                viewGroup.addView(view, params);            }        //这个是View下的一个方法,加载完后会调用一下,自定义View的时候可以用到这个方法        //你可以重写这个方法,来find一些View对象        if (finishInflate) {            parent.onFinishInflate();        }    }

至此所有的递归加载的操作就已经完成了,还记得inflate里边的temp根View么,这个时候他已经是一个完整的View Tree了,都完事后inflate返回出去,你就操作View了


代码不要读,要去不断的修改验证修改验证,才能学得更精

纸上得来终觉浅,绝知此事要躬行

2 0