Public number: byte array, hope to help you 🤣🤣
Context is often used in our daily development. It refers to an abstract class at the code level, a Context environment in concept, and functions to access system services and system resources. Activities, services, and applications all indirectly inherit from Context
Context is an abstract class that defines methods such as startActivity, sendBroadcast, and getSharedPreferences. The general inheritance relationship of Context is shown in the following figure
- ContextImpl and ContextWrapper both inherit directly from the Context, but it’s ContextImpl that implements the parent method. ContextWrapper does not contain the actual implementation logic, but contains a member variable of ContextImpl instance
mBase
Almost all methods in ContextWrapper are calledmBase
The same method to implement, play a role of method transfer, hidden method body concrete implementation logic - ContextThemeWrapper, Service, Application and ReceiverRestrictedContext inherit in ContextWrapper, can pass
mBase
Methods to use Context, and they’re also decorator classes that extend different functionality from ContextWrapper - The Activity needs to support theme-related functionality, so it extends the ContextThemeWrapper from ContextWrapper. Others that do not require theme functionality inherit directly from ContextWrapper
- System has limited the BroadcastReceiver cannot be used to register the broadcast and binding service, so its onReceive method was introduced into the Context object actually belong to ReceiverRestrictedContext type. ReceiverRestrictedContext overloading the registerReceiver and bindService, when invoked directly throw an exception
- As can be seen, the overall implementation relationship of Contex is the use of decorator mode, which extends or limits the function of ContextImpl through combination rather than inheritance. Different decorator classes are selected at run time for specific functional scenarios
A, the Activity
As mentioned earlier, ContextWrapper contains a member variable mBase of ContextImpl, so the Activity also contains it. The timing of an Activity’s mBase initialization depends on the ActivityThread’s performLaunchActivity method, which is used to build the Activity instance when the Activity starts. In addition, we know that the Activity includes a getApplication() method to get an Application instance, When instantiating the Activity, you need to pass the ContextImpl and Application together to the Activity
The performLaunchActivity method steps can be roughly divided into four steps:
- As a first step, create ContextImpl instance through createBaseContextForActivity method, get the appContext
- The second step is to instantiate the Activity with reflection and get the Activity
- Step 3: Get the Application instance, the app
- Step 4: Attach the appContext and app to the activity to initialize the mBase and Application
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {.../ / the first step
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
/ / the second stepactivity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ...}catch(Exception e) {···}try {
/ / the third step
Application app = r.packageInfo.makeApplication(false, mInstrumentation); ...if(activity ! =null) {... appContext. SetOuterContext (activity);/ / step 4
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, r.assistToken); ......}}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;
}
Copy the code
The Attach method of the Activity also assigns values to the mApplication and mBase member variables. MBase and attachBaseContext are declared in ContextWrapper, the parent class
public class Activity extends ContextThemeWrapper {
@UnsupportedAppUsage
Context mBase;
private Application mApplication;
@UnsupportedAppUsage
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, IBinder assistToken) { attachBaseContext(context); ... mApplication = application; ...}protected void attachBaseContext(Context base) {
if(mBase ! =null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public final Application getApplication(a) {
returnmApplication; }}Copy the code
Therefore, the mBase and Application contained in the Activity are obtained during startup. After the Activity is instantiated, its Attach method is called to initialize these two member variables. The Activity inherits directly from ContextThemeWrapper, which in turn inherits directly from ContextWrapper, The ContextImpl that the Activity gets is used to initialize the mBase declared in ContextWrapper, and then the Activity can use the various methods in the Context
Second, the Service
The Service Context creation process is similar to that of an Activity, with the focus on the handleCreateService method of ActivityThread, which is used to create the Service instance and call back its onCreate method
@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {··· Service Service =null;
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
Application app = packageInfo.makeApplication(false, mInstrumentation); ... service = packageInfo. GetAppFactory () instantiateService (cl, data. Info. Name, data. The intent); service.attach(context,this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
} catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}catch (Exception e) {
if(! mInstrumentation.onException(service, e)) {throw new RuntimeException(
"Unable to create service " + data.info.name
+ ":"+ e.toString(), e); }}}Copy the code
After receiving the ContextImpl and Application instances, the Service attach method also initializes its mBase and mApplication member variables, much like an Activity
@UnsupportedAppUsage
public final void attach( Context context, ActivityThread thread, String className, IBinder token, Application application, Object activityManager) { attachBaseContext(context); ... mApplication = application; ...}Copy the code
Third, BroadcastReceiver
The BroadcastReceiver Context creation process is based on ActivityThread’s handleReceiver method, which is used to create the BroadcastReceiver instance and call back its onReceive method. Because the system limits the BroadcastReceiver cannot be used to register the broadcast and binding service, so its onReceive method was introduced into the Context of the object actually belong to a subclass of ContextWrapper ReceiverRestrictedContext
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private void handleReceiver(ReceiverData data) {... Application app; BroadcastReceiver receiver; ContextImpl context;try {
app = packageInfo.makeApplication(false, mInstrumentation); context = (ContextImpl) app.getBaseContext(); ... receiver. = packageInfo getAppFactory () instantiateReceiver (cl, data. Info. Name, data. The intent); }catch(Exception e) {···}try{...//传递的是 ReceiverRestrictedContextreceiver.onReceive(context.getReceiverRestrictedContext(), data.intent); }...}Copy the code
ReceiverRestrictedContext overloading the registerReceiver and bindService directly throw an exception when invoked, limiting the BroadcastReceiver corresponding function
class ReceiverRestrictedContext extends ContextWrapper {
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
if (receiver == null) {
// Allow retrieving current sticky broadcast; this is safe since we
// aren't actually registering a receiver.
return super.registerReceiver(null, filter, broadcastPermission, scheduler);
} else {
throw new ReceiverCallNotAllowedException(
"BroadcastReceiver components are not allowed to register to receive intents"); }}@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException(
"BroadcastReceiver components are not allowed to bind to services"); }...}Copy the code
Fourth, the ContentProvider
ContentProvider is not a subclass of Context, but it’s one of the four components, so let’s talk about it together
The system automatically initializes the ContentProvider and passes in the Context when the application starts, so many third-party open source libraries use ContentProvider to retrieve the Context and initialize itself, making it less intrusive for the party that references the open source library
The ContentProvider Context creation process is based on ActivityThread’s installProvider method, which is used to create the ContentProvider instance. After retrieving the ContextImpl instance, the method retrieves the ContentProvider instance through reflection, and then calls the ContentProvider attachInfo method
@UnsupportedAppUsage
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ":"
+ info.name);
}
Context c = null;
// Get the ContextImpl objectc = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); ...try{...// Instantiate the ContentProvider through reflectionlocalProvider = packageInfo.getAppFactory() .instantiateProvider(cl, info.name); ...// Pass in the Context object
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if(! mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ":" + e.toString(), e);
}
return null; }}else {
provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ":"+ info.name); }...return retHolder;
}
Copy the code
The attachInfo method of the ContentProvider eventually initializes its own mContext variable and then calls back to its own onCreate() method to complete its initialization
@UnsupportedAppUsage
private Context mContext = null;
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {...if (mContext == null) { mContext = context; ... the ContentProvider.this.onCreate(); }}Copy the code
Five, the Application
Review the above code, you can see the Activity, Service, and BroadcastReceiver by LoadedApk. MakeApplication method to get the Application instance, Let’s look at the process of creating an Application Context
The process of the makeApplication method can be divided into four steps:
- The first step is to create ContextImpl instance with the createAppContext method to get the appContext
- The second step initializes the Application’s mBase variable by instantiating the Application by reflection and calling back to its Attach (Context) method, passing in the Context parameter appContext
- Third, save the resulting Application as the global variable mApplication
- Step 4: Call back to Application’s onCreate method
@UnsupportedAppUsage
private Application mApplication;
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if(mApplication ! =null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null; ...try{.../ / the first step
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); .../ / the second step
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch(the Exception e) {...} mActivityThread. MAllApplications. Add (app);/ / the third step
mApplication = app;
if(instrumentation ! =null) {
try {
/ / step 4
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if(! instrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ":" + e.toString(), e);
}
}
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
Copy the code
The Context class also contains a getApplicationContext() method to get the Context of Application type, the logic of which is implemented by ContextImpl. If mPackageInfo is not null, call its getApplication() method to get the Application object saved in step 3 above, otherwise get it through mMainThread
MPackageInfo is not null because the application is already started when the getApplicationContext() method is called in the Activity, Service, or BroadcastReceiver. This is where we get the unique ApplicationContext instance in the current process
@UnsupportedAppUsage
final @NonNull ActivityThread mMainThread;
@UnsupportedAppUsage
final @NonNull LoadedApk mPackageInfo;
@Override
public Context getApplicationContext(a) {
return(mPackageInfo ! =null)? mPackageInfo.getApplication() : mMainThread.getApplication(); }Copy the code