LayoutInflater源码分析(一)

(一)前言

印象最深刻是在Adapter的getView方法中,经常会这样写代码:

 view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_vip, parent, false);

基本没想过原理是什么,只知道最后item_vip会在设备屏幕上显现就是了。知其然不知其所以然,对于立志成为更好的自己来说,这可不是个好现象哦。我们今天就来细细研究一下LayoutInflater相关源码。

我们先单纯的看上面的代码,LayoutInflater的from方法输入了一个xml参数文件,然后输出了View对象。即可以说LayoutInflater的主要功能来说就是布局加载

回想我们写Activity经常用以下代码:

setContentView(R.layout.activity_main);

其实最终也是用LayoutInflater来加载布局的。总不能我说什么就是什么吧,看代码。

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

再点getWindow的setContentView方法:

public abstract void setContentView(@LayoutRes int layoutResID);

发现是抽象方法,而getWindow()指向什么?我们在onCreate中打个Log看看。

Log.d("ala", String.valueOf(getWindow()));

原来Window的子类PhoneWindow实现了setContentView方法。找到PhoneWindow看看里面怎么写的:

 public void setContentView(int layoutResID) {
        //省略部分代码
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }

我追踪了上面的if语句,最终也是调用inflate方法。在这我就不在这分析了,这部分大家自己看代码确认。

还有一点,如果你的Activity继承的是AppCompatActivity,那么点击onCreate方法中setContentView,跳到的代码会有所不同,但是模仿我上述的操作,还是得到想要的信息的。

(二)LayoutInflater的form()方法分析

找到LayoutInflater类,查看其from方法:

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;
    }

点进getSystemService()方法,发现是个abstract抽象方法。那就得找哪个类实现了这个方法,当然是Activity类啦。点进Activity的getSystemService方法:
LayoutInflater源码分析(一)_第1张图片
public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        //大兄弟我们走这!
        return super.getSystemService(name);
    }

因为我们传的参数name值是Context.LAYOUT_INFLATER_SERVICE,所以直接走return super.getSystemService(name);
跳到了ContextThemeWrapper类中的getSystemService方法中:

public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            //俺是个单例耶~~
            if (mInflater == null) {
                //再多看我两眼~
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

程序会走第一个if语句,我们可以看到每个Activity内包含的LayoutInflater是一个单例。最开始mInflater肯定是null,所以又回到了刚开始的LayoutInflater.from()方法:

LayoutInflater.from(getBaseContext()).cloneInContext(this);

不一样的是这次上下文的对象是ContextImpl对象,为什么是ContextImpl呢?看明白我Context及其子类源码分析(一)文章中关于“Activity中ContextImpl实例化源码分析”的人就会知道:每个Activity是在ActivityThread类中的performLaunchActivity方法中开始被创建的,紧接着被实例的activity会实例一个ContextImpl对象,通过attach方法,最终将这个ContextImpl对象赋给Activity的父类ContextWrapper(Activity实际继承了ContextThemeWrapper ,而ContextThemeWrapper 继承自ContextWrapper )中的成员变量mBase对象。上面代码中的getBaseContext()就是指的mBase对象。其实我们可以因此想到,创建了不同的Activity,那他们的mBase也是不同的,即不同Context对象创建的LayoutInflater对象也不同

我们知道一开始mInflater为null,所以又走刚开始的刚开始的LayoutInflater.from()方法,但这个时候,context指的是明确了的ContextImpl,那么去ContextImpl类中看getSystemService方法[1]

   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;
    }
@Override  
public Object getSystemService(String name) {  
        return SystemServiceRegistry.getSystemService(this, name);  
    }  

查找SystemServiceRegistry类的getSystemService()方法:

public static Object getSystemService(ContextImpl ctx, String name) {  
        ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);  
        return fetcher != null ? fetcher.getService(ctx) : null;  
    } 

我们再查一下fetcher.getService的相关代码:

    static abstract interface ServiceFetcher {  
        T getService(ContextImpl ctx);  
    }  
       
    static abstract class CachedServiceFetcher implements ServiceFetcher {  
        private final int mCacheIndex;  
  
        public CachedServiceFetcher() {  
            mCacheIndex = sServiceCacheSize++;  
        }  
  
        @Override  
        @SuppressWarnings("unchecked")  
        public final T getService(ContextImpl ctx) {  
            final Object[] cache = ctx.mServiceCache;  
            synchronized (cache) {  
                // Fetch or create the service.  
                Object service = cache[mCacheIndex];  
                if (service == null) {  
                    service = createService(ctx);  
                    cache[mCacheIndex] = service;  
                }  
                return (T)service;  
            }  
        }  
  
        public abstract T createService(ContextImpl ctx);  
    } 

我们可以看到逻辑上会先去缓存中查找ContextImpl中有没有service,如果没有就createService新建一个。而createService是个抽象方法,找到它的实现类SystemServiceRegistry:

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,  
                new CachedServiceFetcher() {  
            @Override  
            public LayoutInflater createService(ContextImpl ctx) {  
                return new PhoneLayoutInflater(ctx.getOuterContext());  
            }});  

终于是感觉快到头了,最终是实例了PhoneLayoutInflater这么一个对象,即mInflater为null时候,逻辑上最终LayoutInflater.from(getBaseContext())这句话是创建了一个PhoneLayoutInflater实例,但是我们仔细看下前面的代码是这样的:

LayoutInflater.from(getBaseContext()).cloneInContext(this);

cloneInContext()这个方法是做什么的呢?

 /**
     * Create a copy of the existing LayoutInflater object, with the copy
     * pointing to a different Context than the original.  This is used by
     * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
     * with the new Context theme.
     * 
     * @param newContext The new Context to associate with the new LayoutInflater.
     * May be the same as the original Context if desired.
     * 
     * @return Returns a brand spanking new LayoutInflater object associated with
     * the given Context.
     */
    public abstract LayoutInflater cloneInContext(Context newContext);

说的是通过现有的LayoutInflater创建一个新的LayoutInflater副本,唯一变化的地方是指向不同的上下文对象。就是说LayoutInflater.from(getBaseContext())创建了一个PhoneLayoutInflater实例,然后cloneInContext方法根据这个实例又克隆了一个新的。

至此,我们简单地将LayoutInflater类的from方法逻辑走了一遍,后续内容我们再在下一篇文章中继续分析。

[1]ContextImpl类有的人通过AS可能看不到,这里推荐一个网站查看源码:ContextImpl.java

你可能感兴趣的:(LayoutInflater源码分析(一))