The Context underlying principle of Android

Keywords: Mobile Java Android Attribute Database

Context is translated into Chinese as: context; context; background; environment, which we often call "context" in development. From the perspective of Android system: Context is a scenario, representing a process of interaction with the operating system. Context is involved in loading resources, launching activities, acquiring system services, and creating views. From the point of View of program, Context is an abstract class, and Activity, Service, Application and so on are all an implementation of this class.

  • Get AssetManager: getAssets();
  • Get Resources: getResources();
  • Get PackageManager: getPackageManager();
  • Get ContentResolver: getContentResolver();
  • Get the main thread Looper: getMainLooper();
  • Get the Content of Application: getApplication Context ();
  • Get resource files: getText, getString, getColor, getDrawable, getColorStateList;
  • Set the theme and get the theme resource id: setTheme, getTheme ResId;
  • Get the style attribute TypedArray: obtainStyledAttributes();
  • Get the class loader ClassLoader: getClassLoader();
  • Get the application information object ApplicationInfo: getApplicationInfo();
  • Get Shared Preferences: getShared Preferences ();
  • Open FileInputStream: openFileInput();
  • Delete files: deleteFile();
  • Get the file File: getFileStreamPath();
  • Open or create a database: openOrCreateDatabase();
  • Remove or delete databases: moveDatabaseFrom(),deleteDatabase();
  • Start Activity: startActivity(),startActivityAsUser(),startActivityForResult(),startActivities();
  • Register, send and cancel broadcasting: registerReceiver (), sendBroadcast (), sendOrdered Broadcast (), unregisterReceiver ();
  • Start, bind, unbind and stop services: startService(),bindService(),unbindService(),stopService();
  • Get system services: getSystem Service ();
  • Check permissions (Android 6.0 or more): checkPermission();
  • Create Context: createPackageContext() according to the application name;
  • Create Context: create Application Context () based on Application information;
  • Get the display information object Display: getDisplay();

1.ContextImpl, ContextWrapper and Contextext

Context is an abstract class. ContextImpl and ContextWrapper both inherit Context, which implements the abstract method of Context. However, we can see from the code that ContextImpl is the detailed implementation class of Context Abstract method, while ContextWrapper calls the corresponding method of mBase, while mBase is Context. From code tracking, mBase is actually ContextImpl, so ContextWra Impl The pper ultimately calls the implementation method in ContextImpl. That is to say, any method we call in ContextImpl is processed in ContextImpl, so when we trace the code, we just need to go to ContextImpl to see the corresponding method processing. The above is just an introduction. Next, we will analyze how to implement it according to the specific code.

From the ContextWrapper code, we can see that only the constructor, attachBaseContext method and getBaseContext method are not copying methods. The other methods are copying methods:

[ContextWrapper.java]

    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;
    }

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }

We can see that only the constructor and the attachBaseContext method pass in mBase. Then we can see from the attachBaseContext method that if mBase exists and calls this method, it throws an exception. So we know that if this method is called, then the constructor can't pass in this value. Let's see where the attachBaseContext method is invoked, and the code You can see that Application, activity, and service all call this method. First, let's look at the code in Application:

[Application.java]

    /**
     * @hide
     */
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

The attachBaseContext method is called in the attach method in Application, and the parameter context is also passed in through the attach method. Then we trace the attach method:

Called in the Instrumentation class:

[Instrumentation.java]

    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

The above method calls are:

[Instrumentation.java]

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }

As you can see from the code, it was called in the new Application, so let's see where to call this method:

[LoadedApk.java]

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        ...

        try {
            ...
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            ...
        }
        ...
        return app;
    }

The above method is called in the LoadedApk class. We don't analyze this class first. We will discuss this process in detail later. Let's first analyze the above code. We can see that we create ContextImpl by calling the ContextImpl.createAppContext method, and then pass the parameters into the new Application method. So we can see that the mBase above is ContextImpl, and then A. Activeness and Service. Let's first analyze Service, because we can see from the diagram that both Service and Application inherit ContextWrapper directly, while Activity inherits ContextThemeWrapper, ContextThemeWrapper inherits ContextWrapper.

[Service.java]

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);
        ...
    }

The attachBaseContext method is called in the attach method in Service, and then look at the call of the attach method:

[ActivityThread.java]

private void handleCreateService(CreateServiceData data) {
        ...
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
         ...
    }

Here we see that the incoming context is ContextImpl, which is verified. Next we also see the service.onCreate method. We see that we call the attach method first and then the onCreate method.

Finally, let's look at Activity. From the diagram above, we can see that Activity does not inherit ContextWrapper directly, but ContextThemeWrapper inherited, ContextThemeWrapper inherited ContextWrapper. From the name, we can see that ContextTheme Wrapper contains information about the theme. It is not difficult to understand that only Activity has an interface for the four components, and the others have no interface. Therefore, Activity needs topic information to show different interface effects. In ContextThemeWrapper, we see an override of the attachBaseContext method, in which only one line of code is to call the attachBaseContext method of the parent class. As follows:

[ContextThemeWrapper.java]

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

There is only one method in Activity that calls this method. Look at the code:

[Activity.java]

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) {
        attachBaseContext(context);

        ...
    }

Let's follow the attach method and see the code:

[ActivitThread.java]

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...
        
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                ...
                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);

                ...
            }
            ...
        return activity;
    }

The method performLaunchActivity is actually the way to start Activity. Let's not talk about it for a moment. We'll explain it in detail later. Let's straighten out Context first. From the above code, we can see that the Context passed in here is created by the createBaseContextForActivity method. Let's take a look at this method.

[ActivitThread.java]

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        ...

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        ...
        return baseContext;
    }

From the above code, I can clearly see that baseContext is appContext assignment, and appContext is ContextImpl, so Context in Activity is ContextImpl.

 

Posted by swampster on Thu, 16 May 2019 04:30:46 -0700