LayoutInflater.from参数Context传Activity、Application区别

3,010 阅读2分钟

关于Context,首先想到的是生命周期的不同。今天学习LayoutInflater查看源码,才发现具体的区别。

LayoutInflater部分源码
    /**
     * Obtains the LayoutInflater from the given 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() 下面看下getSystemService源码

  1. 在Context.java中是抽象方法
  2. Context的子类是ContextWrapper
  3. ContextWrapper的子类ContextThemeWrapper
  4. Activity继承ContextThemeWrapper,Application继承ContextWrapper
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient {
        
public class Application extends ContextWrapper 
        implements ComponentCallbacks2 {
  1. 先看下ContextWrapper的源码
/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    
    @Override
    public Object getSystemService(String name) {
        return mBase.getSystemService(name);
    }
}

mBase具体的实现类是ContextImpl,getSystemService返回的Inflater对象是PhoneLayoutInflater。

  1. 下面看下ContextThemeWrapper的源码
public class ContextThemeWrapper extends ContextWrapper {
    public Object getSystemService(String name) {
        if ("layout_inflater".equals(name)) {
            if (this.mInflater == null) {
                this.mInflater = LayoutInflater.from(this.getBaseContext()).cloneInContext(this);
            }
            return this.mInflater;
        } else {
            return this.getBaseContext().getSystemService(name);
        }
    }
}

this.getBaseContext()返回的也是ContextImpl对象,所以LayoutInflater.from(this.getBaseContext())返回的也是PhoneLayoutInflater。 接着调用了cloneInContext(this),设置Activity对象Clone给了Inflate对象中的Context。 而Activity.setContentView()会调用PhoneWindow.setContentView(),因为Activity继承ContextThemeWrapper,所以会解析Theme和Resource等数据。

  1. 接下来看下PhoneWindow源码
/**
 * Android-specific Window.
 * <p>
 * todo: need to pull the generic functionality out into a base class
 * in android.widget.
 *
 * @hide
 */
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }
    
    @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);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
    
    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
}

可见,在创建基本DecorView时,会给Context设置Theme属性。

  1. ContextImpl源码
/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {
    ......省略......
    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
    ......省略......
}
/**
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 */
final class SystemServiceRegistry {
    registerService(Context.LAYOUT_INFLATER_SERVICE,     LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
}

以上,当LayoutInflater.from(Context context) context 是 Application实例,不会包含Theme等 context 是 Activity实例,会包含Theme等

最后看下生成的LayoutInfalter对象截图

public class InflateActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inflate);

        LayoutInflater.from(this);
        LayoutInflater.from(getApplicationContext());
    }
}