Different Context objects create different LayoutInflater objects

Keywords: Android

Create LayoutInflater objects. According to the context objects that are uploaded, the LayoutInflater objects are created differently. The LayoutInflater objects created in different activities are also different. Let's take a look at them first.

 Activity Acquisition in LayoutInflater Object and Printed Object Address
LayoutInflater.from(this);   com.android.internal.policy.impl.PhoneLayoutInflater@41882b90

LayoutInflater.from(getApplication());   com.android.internal.policy.impl.PhoneLayoutInflater@418da098
        
LayoutInflater.from(getBaseContext());   com.android.internal.policy.impl.PhoneLayoutInflater@41882b40
        
getSystemService(Context.LAYOUT_INFLATER_SERVICE);   com.android.internal.policy.impl.PhoneLayoutInflater@41882b90
        
getApplication().getSystemService(Context.LAYOUT_INFLATER_SERVICE);com.android.internal.policy.impl.PhoneLayoutInflater@418da098
        
getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);com.android.internal.policy.impl.PhoneLayoutInflater@41882b40
         
getBaseContext();   android.app.ContextImpl@41882338
        
getApplication().getBaseContext();   android.app.ContextImpl@41870230


        //Getting the LayoutInflater object and the printed object address in another Activity


LayoutInflater.from(this);   com.android.internal.policy.impl.PhoneLayoutInflater@4189f2f0

LayoutInflater.from(getApplication());   com.android.internal.policy.impl.PhoneLayoutInflater@418da098
        
LayoutInflater.from(getBaseContext());   com.android.internal.policy.impl.PhoneLayoutInflater@4189f2a0
        
getSystemService(Context.LAYOUT_INFLATER_SERVICE);   com.android.internal.policy.impl.PhoneLayoutInflater@4189f2f0
        
getApplication().getSystemService(Context.LAYOUT_INFLATER_SERVICE);com.android.internal.policy.impl.PhoneLayoutInflater@418da098
        
getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);com.android.internal.policy.impl.PhoneLayoutInflater@4189f2a0
         
getBaseContext();   android.app.ContextImpl@4189ecd8
        
getApplication().getBaseContext();   android.app.ContextImpl@41870230

According to the implementation of the form method of LayoutInflate, in fact, LayoutInflater. from (this); LayoutInflater. from (getApplication (); LayoutInflater. from (getBaseContext (); and getSystemService (Context. LAYOUT_INFLATER_SERVICE); getSystem Application (Context. LAYOUT_INFLATER_SERVICE); getContext (); (Context. So they get the same object, so why don't they get different LayoutInflater objects from different context objects, because mBase (ContextImpl) objects in different context objects are different, LayoutInflater is created in ContextImpl, so the objects created are different.

Let's take a look at the LayoutInflater creation process. Let's track the code. Take LayoutInflater.from(this) for example, look at the form() method in LayoutInflater.

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;
    }
In this method, the getSystemService() method of Context is invoked, because this is passed, that is, the Activity object, and the Activity is also the Context object. Their inheritance relationship is: Activity - > ContextThemeWrapper - > ContextWrapper - > Context. So to see if the subclass of Context overrides the getSystemService() method, it happens that ContextThemeWrapper overrides and Activity overrides, but it has nothing to do with LayoutInflater, so let's look at the getSystemService() method in ContextThemeWrapper.

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);
    }
At the beginning, mInflater must be for null, executing the LayoutInflater.from(getBaseContext()) method, and returning to the original LayoutInflater.from() method. The difference is that the object of this context is the ContextImpl object, why is it the ContextImpl? Let's look at the getBaseContext() method.

public Context getBaseContext() {
        return mBase;
    }
This return only returns mBase. Let's see where it's assigned. It's assigned in the attachBaseContext method of the ContextWrapper class.

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
Where is this method called? It's called in Activity's attach method.

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);
	......
    }
Where is attach called, in ActivityThread

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ......

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
		......
	    }
	......
}
appContext is returned by the createBaseContextForActivity(r, activity) method.

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        ......
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if (pkgName != null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if (id != Display.DEFAULT_DISPLAY) {
                    Display display =
                            dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
                    baseContext = appContext.createDisplayContext(display);
                    break;
                }
            }
        }
        return baseContext;
    }
The baseContext is also obtained by the createActivityContext() method of ContextImpl.

static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread, packageInfo, null, null, false,
                null, overrideConfiguration, displayId);
    }
See, the returned object is new ContextImpl, so the previous getBaseContext() gets the ContextImpl object. So the ContextImpl objects in the Context of different objects are different, so the LayoutInflate objects created may be different. Look down


So let's look at the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) statement in LayoutInflater.from(), which is the ContextImpl object, and see how it works.

@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
GetSystem Service Method of System Service Registry

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

static abstract interface ServiceFetcher<T> {
        T getService(ContextImpl ctx);
    }

   
    static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
        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);
    }
It gets it from cache first, and if it doesn't, it creates createService, because it's abstract, so look at its implementation class.

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
At last, I saw the LayoutInflater object PhoneLayoutInflater, which is not the final object of LayoutInflater.from(this). Look at the mInflater = LayoutInflater. From (getBaseContext (). CloneInContext (this); in fact, when he got the object, he clone d another one.

public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }
Another PhoneLayoutInflater object came out, which is the LayoutInflate object returned by LayoutInflater.from(this)
Let's take a look at the LayoutInflater.from(getBaseContext()) to get the LayoutInflater way. Where have we seen this feeling? That's right in the getSystemService method in ContextThemeWrapper

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);
    }
In fact, the LayoutInflater.from(getBaseContext()) acquisition of the LayoutInflate object is the LayoutInflater.from(this) acquisition of the LayoutInflate object created by the LayoutInflate object when it did not execute the cloneInContext(this) method. Another is LayoutInflater. from (getApplication(); the way to get LayoutInflate objects, because getApplication() gets global Application objects, and its internal ContextImpl objects are different, so the resulting LayoutInflate objects are different from the previous two, and that is, regardless of which Activity getApplication() obtains the same object. So LayoutInflater.from(getApplication()); the acquired LayoutInflate object is the same.

Okay, that's why different Context objects get LayoutInflater from different objects. From the initial getBaseContext(); android.app.ContextImpl@41882338, you can see that getBaseContext() takes the ContextImpl object, and getApplication().getBaseContext(); android.app.ContextImpl@41870230 takes the same ContextImpl object.

Posted by rbastien on Sun, 24 Mar 2019 03:42:28 -0700