What is the Context of Application, Activity and Service in Android?

Posted by djroki on Sun, 16 Jan 2022 15:08:18 +0100

PS: This article is a reprint of the article. Reading the original text will be more readable. There is a link to the original text at the end of the article

The real let nature take its course is not demanding after doing our best, not inaction with both hands.

ps: the source code of this article is analyzed based on Android Api 26.

Is it a little unclear to see the title of the article? Do you think the author doesn't know what to ask? It doesn't matter if we don't understand it. Now let's analyze it step by step; List the contents first;

catalogue

1. getString(@StringRes int resId) method

 1,1 Application of getString(@StringRes int resId) method

 1,2 Activity of getString(@StringRes int resId) method

 1,3 Service of getString(@StringRes int resId) method

2. mBase variable

 2,1 Application of mBase What are the variables

 2,2 Activity of mBase What are the variables

 2,3 Service of mBase What are the variables


1. getString(@StringRes int resId) method

1. 1 getString(@StringRes int resId) method of application

In the actual development of Android, we sometimes use the getString(@StringRes int resId) method of Application, Activity and Service. They are all implemented through the getString(@StringRes int resId) method of Resources, but which object do they use to obtain Resources? Let's first look at the getString(@StringRes int resId) method of Application. Because Application inherits from ContextWrapper and ContextWrapper inherits from Context, the getstri ng (@ stringres int resid) method of Application is in Context;

@NonNull
public final String getString(@StringRes int resId) {
    
    //1,
    return getResources().getString(resId);
}

Look at the getResources method in note 1. It is an abstract method, which is specifically implemented in the ContextWrapper class;

@Override
public Resources getResources() {
    
    //2,
    return mBase.getResources();
}

Look at note 2. What is the mBase of Application? We'll save the suspense here for later analysis.

1. 2 getString(@StringRes int resId) method of activity

The getString(@StringRes int resId) method of the Activity is the same as the get string (@ stringres int resid) method of the Application. The analysis of the Activity will not continue here.

1. 3. getString(@StringRes int resId) method of service

The getString(@StringRes int resId) method of the Service is the same as the get string (@ stringres int resid) method of the Application. The analysis of the Service will not continue here.

2. mBase variable

2. 1. What is the mBase variable of the application

We know that ActivityThread is a program entry class. Each application corresponds to an ApplicationThread object. It is a Binder object and an internal class of ActivityThread. Its function is to receive remote messages from ActivityManagerService. Suppose we start the App for the first time, ActivityManagerService will send a startActivity message, The scheduleLaunchActivity method in the internal class ApplicationThread of ActivityThread (I won't write out many parameters of this method. See the code posted below for details) will be used;

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                                         ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                                         CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                                         int procState, Bundle state, PersistableBundle persistentState,
                                         List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                                         boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    ......
    //3,
    sendMessage(H.LAUNCH_ACTIVITY, r);
}

Look at the code in note 3. Here, send an H.launch_ Information marked by activity. At this time, the internal class H of ActivityThread will process the message;

private class H extends Handler {

    ......
    public void handleMessage(Message msg) {
        ......
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);

                //4,
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            ......
        }
        ......

}

Look at the code in note 4. Here, the handlelaunch activity (activityclientrecord R, intent, customintent, string reason) method of ActivityThread is called;

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ......
        //5,
        Activity a = performLaunchActivity(r, customIntent);
        ......
 }

Look at the code in note 5. Here is the method for creating an Activity. Follow the performlaunchactivity (activityclientrecord R, intent, customintent) method of the Activity to see;

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
    ......
    //6,
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    ......
    try {
        //7,
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        ......
        if (activity != null) {
            ......
            //8,
            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, window, r.configCallback);
            ......
        }
        ......
    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to start activity " + component
                            + ": " + e.toString(), e);
        }
    }

    return activity;

}

Look at the code in note 7. r.packageInfo is an object of LoadedApk type. The makeapplication (Boolean forcedefaultappclass, instrumentation) method of LoadedApk is to create an Application object;

public Application makeApplication(boolean forceDefaultAppClass,

                                   Instrumentation instrumentation) {
    ......
    Application app = null;
    ......
    try {
        ......
        //9,
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        
        //10,
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        ......
    }
    ......
    return app;

}

Look at the code in note 9. ContextImpl inherits from Context. Let's take a look at the creation process of the specific implementation class of ContextImpl (that is, the subclass of ContextImpl), that is, the createAppContext(ActivityThread mainThread, LoadedApk packageInfo) method of Context impl;

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {

    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
    
    //11,
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
            null);
    context.setResources(packageInfo.getResources());
    return context;

}

It can be seen from the code in note 11 that the specific implementation class of ContextImpl is itself; OK, let's go back to the code in comment 10, m activity thread M Instrumentation represents an object of Instrumentation type. Here, the ContextImpl object is used as one of the parameters to call the newapplication (classloader Cl, string classname, context, context) method of Instrumentation;

public Application newApplication(ClassLoader cl, String className, Context context)

        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    
    //12,
    return newApplication(cl.loadClass(className), context);
}

Look at the code in note 12. The newapplication (classloader Cl, string classname, context) method of Instrumentation calls the newapplication (class <? > clazz, context context) method of Instrumentation;

static public Application newApplication(Class<?> clazz, Context context)

        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();
    
    //13,
    app.attach(context);
    return app;
}

Look at the code in note 13. The newapplication (class <? > clazz, context) method of Instrumentation calls the attach(Context context) method of Application;

/ package / final void attach(Context context) {

    //14,
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

Looking at the code in note 14, the attach(Context context) method of Application calls the attachBaseContext(Context base) method of ContextWrapper (because Application inherits from ContextWrapper);

protected void attachBaseContext(Context base) {

    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;

}

See, the ContextImpl object initialized by the code in note 11 is assigned to the Application's mBase variable. It can be seen that the Application's mBase variable is essentially a ContextImpl object.

2. 2. What is the mBase variable of activity

Return to the code in Note 6, that is, the createbasecontextfor activity (activityclientrecord R) method of ActivityThread;

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {

    ......
    //15,
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
    ......
    return appContext;
}

Look at the code in note 15. This is also the implementation of creating ContextImpl object. Is it the specific object of creating ContextImpl subclass? Or create a ContextImpl object? Let's look down at the createActivityContext method of ContextImpl (I won't list too many parameters. See the following code for details);

static ContextImpl createActivityContext(ActivityThread mainThread,

                                         LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
                                         Configuration overrideConfiguration) {
   ......
    //16,
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
            activityToken, null, 0, classLoader);
    ......
    return context;
}

Look at the code in note 16. It creates a ContextImpl object and returns it. It returns to the code in Note 6, so appContext is a ContextImpl object, not a specific object of ContextImpl subclass; OK, let's go back to the code at comment 8. Here, take appContext as one of the parameters to call the attach method of Activity (there are too many parameters here, so I won't list them. See the following code for details);

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,
                  Window window, ActivityConfigCallback activityConfigCallback) {
    //17,
    attachBaseContext(context);
    ......
}

Looking at the code in note 17, the attach method of the Activity calls the attachBaseContext(Context newBase) method of the ContextThemeWrapper (because the Activity inherits from the ContextThemeWrapper);

@Override
protected void attachBaseContext(Context newBase) {
    
    //18,
    super.attachBaseContext(newBase);
}

Look at the code in note 18. super here refers to ContextWrapper. The attachBaseContext(Context newBase) method of ContextThemeWrapper calls the attachBaseContext(Context base) method of ContextWrapper;

protected void attachBaseContext(Context base) {

    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

Ha ha, you see, the ContextImpl object initialized by the code in Note 6 is assigned to the Activity's mBase variable. It can be seen that the Activity's mBase variable is also a ContextImpl object in essence.

2. 3. What is the mBase variable of service

In fact, the message of creating a Service received by the ApplicationThread is very similar to that of the Activity. Here, we will no longer analyze it from the message entry of creating a Service received by the ApplicationThread. We will directly analyze it from the method of creating a Service, that is, the handlecreateservice (createservicedata) method of the ActivityThread;

private void handleCreateService(CreateServiceData data) {

    ......
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        
        //19,
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                            + ": " + e.toString(), e);
        }
    }

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        //20,
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        
        //21,
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        ......
    } catch (Exception e) {
       ......
    }
}

Look at the code in note 19 and create a service object through the reflection mechanism; The code of note 20 finally creates a contextimpl object. The specific implementation is the same as the code of note 9, which has been analyzed earlier; Look at the 21 annotated code, take the ContextImpl object as one of the parameters, and then call the attach method of Service (too many parameters, inconvenient to enumerate, see the code below).

public final void attach(

        Context context,
        ActivityThread thread, String className, IBinder token,
        Application application, Object activityManager) {
    
    //22,
    attachBaseContext(context);
    ......
}

Looking at the code in note 22, the attach method of the Service calls the attachBaseContext(Context base) method of the ContextWrapper (because the Service inherits from the ContextWrapper);

protected void attachBaseContext(Context base) {

    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

Haha, see again. The code initialized ContextImpl object at comment 20 is assigned to the Service's mBase variable. It can be seen that the Service's mBase variable is also a ContextImpl object in essence.

Summary: ha ha, now you can answer what the Context of Application, Activity and Service in Android really works. It is not a ContextWrapper, but a proxy. What really works is ContextImpl.

This article ends here. Due to the limited technical level, mistakes will inevitably be made in the article. You are welcome to criticize and correct.

Topics: Java Android