LayoutInflater.from參數Context傳Activity、Application區別

關於Context,首先想到的是生命週期的不一樣。今天學習LayoutInflater查看源碼,才發現具體的區別。java

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源碼android

  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。bash

  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等數據。app

  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屬性。ide

  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對象截圖ui

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());
    }
}
複製代碼

相關文章
相關標籤/搜索