从Activity setContentView了解LayoutInflater(一)

简介

      驱使我去了解LayoutInflater的原动力是五月份在写热插件demo时,总是会在资源加载出一些乱起糟的异常,所以就把资源从文件加载到内存的整个流程熟悉了一边,顺带也解决了刚入门android时,对setContentView是如何将xml转换成View的好奇。


正文

       从activity的setContentView开始

       先看下setContentView(int res)的源码(android 5.1):

public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

       使用过DecorView的都应该挺熟悉getWindwow这个方法的,返回的是一个Window对象的引用,实例是PhoneWindow,进入PhoneWindow去看下setContentView:

 @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
这里的前几行并不需要我们关心,installDecor是对DecorView进行初始化使用的,FEATURE_CONTENT_TRANSITIONS这是专场动画标记,最关心的其实是mLayoutInflater.inflate(layoutResID,mContentParent);

这里有两个关键点:

1、mLayoutInflater是从哪里来的?

2、inflate方法是如何处理资源id,并将其以view的形式加入到mContentParent(mContentParent的英文解释:This is the view in which the window contents are placed. It is either mDecor itself, or a child of mDecor where the contents go.)

1、mLayoutInflater是从哪里来的?

     在源码中找到mLayoutInflater被赋值的位置:

    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }
      mLayoutInflater在PhoneWindow中被初始化,那么from方法又是如给mLayoutInflater进行赋值的呢,继续往下看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就充满了熟悉感,毕竟对系统服务的调用就是通过getSystemService进行实现的,那么就进 ContextImpl(Context的实现类)中一探究竟:

@Override
    public Object getSystemService(String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }

      关于ServiewFetcher不做过多描述,可以当作一个生成service的简单工厂,源码有兴趣的话可以去看一下。

      这里需要注意的是这个SYSTEM_SERVIEC_MAP:

private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
            new HashMap<String, ServiceFetcher>();

      在翻下是在哪里进行的属性添加:

private static void registerService(String serviceName, ServiceFetcher fetcher) {
        if (!(fetcher instanceof StaticServiceFetcher)) {
            fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
        }
        SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
    }
static {
        ......


        registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
                }});
        ......
       
       }
        在这里可以看到ServiceFetcher是在类加载之后就立马加入到map中的,那么想要再往走,就要进入到PolicyManager中:

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {
        // Pull in the actual implementation of the policy at run-time
        try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
        } catch (InstantiationException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
        }
    }

    // Cannot instantiate this class
    private PolicyManager() {}

    // The static methods to spawn new policy-specific objects
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }

    public static WindowManagerPolicy makeNewWindowManager() {
        return sPolicy.makeNewWindowManager();
    }

    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}
       PolicyManager类就是个简单工厂中的工厂类,其接口类是IPolicy,实现类是Policy,有代码也可以看的,Policy是通过类加载器进行加载并初始化的,所以真正的逻辑代码还在Policy中:

public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }
       在这里只是简单的创建了一个PhoneLayoutInflater对象,这就有意思了,我们要的明明是LayoutInflater,那这个类必然是其子类,猜一下想必构造方法也所差无几:

public class PhoneLayoutInflater extends LayoutInflater {
    private static final String[] sClassPrefixList = {
        "android.widget.",
        "android.webkit.",
        "android.app."
    };
    
    /**
     * Instead of instantiating directly, you should retrieve an instance
     * through {@link Context#getSystemService}
     * 
     * @param context The Context in which in which to find resources and other
     *                application-specific things.
     * 
     * @see Context#getSystemService
     */
    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 protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    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);
    }
}

       此类重写了onCreatView方法,作用就是在onCreatView之前便利webkit、app、widget这几个包下是否有对应的View存在,至于初始化什么的自然和LayoutInflater一样了,也就是说我们在这里可以把它就看作是一个标准的LayoutInflater!
       这样就清楚了,虽然过程有点绕,但实际上就相当于在activty中新建了一个LayoutInflater,然后通过inflate方法将res_id转换成相应的View加入到对应的容器布局(mContentView)中。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值