LayoutInflater加载xml布局源码分析及View的mParent的初始化

上一篇文章: setContentView源码分析

在上一篇文章到最后加载我们自己的xml布局文件到父布局中:

LayoutInflater.from(this.mContext).inflate(resId, contentParent);

那么LayoutInflater是如何加载xml布局文件的呢?

以下源码基于android28



1. LayoutInflater.from(Context)

我们先来看一下 LayoutInflater.from(Context) 拿到的是什么。

public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

返回的是这个 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
那我们就要去Context里面去找

public abstract class Context {
	......
	public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
	......
}
✍我们可以看到Context是个抽象类,getSystemService也是一个抽象方法,那这个方法的具体实现在哪里呢?
这个我们就要去看ContextImp,它是Context的具体实现类
ps: ActivityServiceApplication都是Context的间接实现类:
			   ContextContextWrapper  
			      ⇣
			  一些中间类
			      ⇣
		ActivityServiceApplication
	它们和ContextImp没有直接关系,它们都继承了ContextWrapper,而ContextWrapper中有一个mBase
	就是ContextImp对象,所以它们都是它们是通过ContextWrapper使用mBase进行进行操作
	
class ContextImpl extends Context {

 	@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
    
}

接着去看SystemServiceRegistry类


final class SystemServiceRegistry {
	private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();
            
     ✍静态代码块 类加载就会被初始化
     	就是把(LAYOUT_INFLATER_SERVICE,CachedServiceFetcher)键值对保存到SYSTEM_SERVICE_FETCHERS中       
	{
		...
 		registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
        ...    
	}
	
	private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }
    
    public static Object getSystemService(ContextImpl ctx, String name) {
    	 ✍ fetcher就是初始化时保存的CachedServiceFetcher
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
    ✍ 接着我们就去找CachedServiceFetcher#getService
        CachedServiceFetcherSystemServiceRegistry的静态内部类
	static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
		 @Override
        @SuppressWarnings("unchecked")
        public final T getService(ContextImpl ctx) {
            final Object[] cache = ctx.mServiceCache;
            final int[] gates = ctx.mServiceInitializationStateArray;

            for (;;) {
                boolean doInitialize = false;
                synchronized (cache) {
                    // Return it if we already have a cached instance.
                    ✍如果我们已经有缓存的对象直接返回
                    T service = (T) cache[mCacheIndex];
                    if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
                        return service;
                    }
 				}

                if (doInitialize) {
                    // Only the first thread gets here.

                    T service = null;
                    @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
                    try {
                    	✍使用createService方法创建一个新的对象
                        service = createService(ctx);
                        newState = ContextImpl.STATE_READY;

                    } catch (ServiceNotFoundException e) {
                        onServiceNotFound(e);

                    } finally {
                        synchronized (cache) {
                        	✍缓存这个对象
                            cache[mCacheIndex] = service;
                            gates[mCacheIndex] = newState;
                            cache.notifyAll();
                        }
                    }
                    return service;
                }
         ✍createService怎么是个抽象方法,具体实现在哪呢?
       public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
            }
        }
	}
}

 ✍还记得上面呢静态代码块中我们registerService的代码么:
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
           			 @Override
            		 public LayoutInflater createService(ContextImpl ctx) {
                		return new PhoneLayoutInflater(ctx.getOuterContext());
           			 }
            	}
            );
     createService()方法是在这里实现的,返回一个PhoneLayoutInflater对象。

我们来看一下PhoneLayoutInflater类

public class PhoneLayoutInflater extends LayoutInflater {
	✍我们xm布局中的控件就存在这三个包中
    private static final String[] sClassPrefixList = {
        "android.widget.",
        "android.webkit.",
        "android.app."
    };

 
    public PhoneLayoutInflater(Context context) {
        super(context);
    }

    protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
        super(original, newContext);
    }

    /** Override onCreateView to instantiate names that correspond to the
        widgets known to the Widget factory. If we don't find a match,
        call through to our super class.
    */
    @Override<<重写了LayoutInflater的onCreateView方法>>
    protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
            	 ✍这里就是把prefix即包名前缀和name一起调用父类LayoutInflater里面的
            	 	createView方法。
                View view = createView(name, prefix, attrs);
                if (view != null) {
                 ✍ view不为空即匹配到正确的包名,结束for循环
                    return view;
                }
            } catch (ClassNotFoundException e) {
                // In this case we want to let the base class take a crack
                // at it.
            }
        }

        return super.onCreateView(name, attrs);
    }

    public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }
}

看了之后,就这?里面也没啥。

2. inflate(resId, contentParent)

LayoutInflater.from(Context) 最终得到PhoneLayoutInflater对象,但是我们上面看到PhoneLayoutInflater里面也没干啥,所以 PhoneLayoutInflater.inflate(resId, contentParent)
还是要看去看其父类LayoutInflater是如何实现的。

public abstract class LayoutInflater {
	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();
        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();
        }
    }
    ✍ 最终调用这个方法
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

               

                final String name = parser.getName();

           
                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, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    ✍创建我们xml布局文件的根布局View,就是最外层View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    
                    // Inflate all children under temp against its context.
                    ✍ 核心代码 把xml布局文件中所有view逐层添加到上面创建的根布局ViewrInflateChildren(parser, temp, attrs, true);

                  

                    ✍如果我们LayoutInflater.from().inflate()传入root布局不为null
                      且是否添加到root布局的boolean值为true,就把我们的xml布局添加到根布局中	
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                   ✍如果root布局为null,或者是否添加到root中的开关boolean值为false,
                   	  就把我们的xml布局添赋值给result,到最后return返回	
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                ......
            }
            return result;
        }


		✍  记住这个 rInflateChildren 方法,它其实是调用了 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;
        boolean pendingRequestFocus = false;while循环逐层获取我们的View标签
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
			
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
            	✍前面几种都是我们常见的include、merge等情况的处理,我们主要看下面普通布局
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                ✍这个不就是上面的那个rInflateChildren方法么,他又会调用rInflate,就是这样的循环添加
                   直到最里层while结束,然后逐层返回添加到其上一层布局中
                rInflateChildren(parser, view, attrs, true);
               ✍这里有一个很重要的点,文章最后会说
                viewGroup.addView(view, params);
            }
        }
      }
    }

我们来看一下createViewFromTag

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");
        }
 		。。。。。。。

        try {
            View view;
            
            。。。。。。。
            
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                    	✍ 如果name不包含 '.' 就像<Button></Button>
                    	此时会调用PhoneLayoutInflater重载的onCreateView方法
                    	就是去加上一个前缀包名,然后再去调用LayoutInflater#createView
                        view = onCreateView(parent, name, attrs);
                    } else {
                    	✍如果name包含'.' 就直接调用LayoutInflater#createView
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } catch (InflateException e) {
            throw e;

        } catch (ClassNotFoundException e) {
            。。。
        } catch (Exception e) {
            。。。
        }
    }

那我们就去看看LayoutInflater#createView方法是如何实现的

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
                ✍ 根据前缀prefix是否为null,拼接完整类所在路径,创建Class
                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中缓存下来
                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 lastContext = mConstructorArgs[0];
            if (mConstructorArgs[0] == null) {
                // Fill in the context if not already within inflation.
                mConstructorArgs[0] = mContext;
            }
            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]));
            }
            mConstructorArgs[0] = lastContext;
            return view;

        } catch (NoSuchMethodException e) {
          ...

        } catch (ClassCastException e) {
          ...
        } catch (ClassNotFoundException e) {
          ...
        } catch (Exception e) {
            ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

这样循环读取标签,然后通过反射创建对象;
通过rInflateChildren循环添加到整个View树中。
这样整个xml文件就加载完成。

3. 附

 viewGroup.addView(view, params);

我们来看一下这个将View添加到其外一层布局控件中的代码:

 	@Override
    public void addView(View child, LayoutParams params) {
        addView(child, -1, params);
    }
    
    public void addView(View child, int index, LayoutParams params) {
         .......
        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }
    private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        
        // tell our children 
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
        	✍添加其下一层时会把自己赋值给其mParent 
            child.mParent = this;
        }
         
    }

这样里层都持有了其外一层的对象。
这个 mParent其实是位于View中,而ViewGroup继承View

public class View implements ...{
    protected ViewParent mParent;
	void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
        } else if (parent == null) {
            mParent = null;
        } else {
            throw new RuntimeException("view " + this + " being added, but"
                    + " it already has a parent");
        }
    }
	public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
            .....
			final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                ✍当这p就是mParent
                p.invalidateChild(this, damage);
            }
          ......
     }
	
 }

✍而mParent是在哪初始化的

		if (preventRequestLayout) {
			✍通过assignParent方法赋值
            child.assignParent(this);
        } else {
        	✍直接赋值 
            child.mParent = this;
        }

就是在我们添加xml布局ViewGroup.addView的时候初始化的

这个mParent在requestLayout;invalidate等更新布局逐层向上传递时有重要作用!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值