Android programs are not like Java programs, just create a class and write a main() method to run. Android application model is based on the application design mode of components. Components should be run in a complete Android engineering environment. System components such as activities and services can work properly, and these components cannot be created in the normal Java object creation way. Instead, they must have their own Context, that is, the Context we discuss here. Context is used to load resources, start activities, obtain system services, and create views.
What exactly is Context
An Activity is a Context, and a Service is a Context. Android programmers abstract the “scene” into a Context class, thinking that every interaction between a user and the operating system is a scene, such as a phone call or a text message, which is a scene with an interface, and some scenes without an interface, such as a Service running in the background. An application can be thought of as a work environment that users in this environment will switch to a different scene, it’s like a secretary at the front desk, she may need to receive guests, may have to print documents, may also to answering customer calls, the call it different scenarios, the front desk secretary could be called an application.
Comments in source code
/** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-callsfor application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
}
Copy the code
It describes information about an application environment (that is, context); Is an abstract class. Android provides a concrete implementation of this abstract class. It allows you to retrieve application resources and classes (including application-level actions such as starting an Activity, broadcasting, accepting an Intent, and so on). Since Context is an abstract class, there must be an implementation class for it. We can view its subclasses in the source code of Context by using the IDE.
The Context class itself is a pure Abstract class with two concrete implementation subclasses: ContextImpl and ContextWrapper.
The ContextWrapper class, as its name suggests, is just a wrapper, and the ContextWrapper constructor must contain a real Context reference, AttachBaseContext () is provided in ContextWrapper to specify the actual Context in the ContextWrapper object. Any method that calls the ContextWrapper object is redirected to the actual Context it contains. The ContextThemeWrapper class, as its name suggests, contains an interface associated with the Theme, as in androidManifest.xml via Android: Theme Specifies the theme for the Application element or Activity element. Of course, only an Activity needs a theme. A Service does not need a theme because it is a background scene with no interface, so the Service directly inherits from ContextWrapper, as does the Application.
The ContextImpl class actually implements all of the functions in the Context, and the implementation of the various Context class methods that are called in the application comes from this class. ContextImpl is the implementation class of the Context, and ContextWrapper is the wrapper class of the Context. Activity, Application, and Service inherit from ContextWrapper, ContextThemeWrapper, ContextWrapper. However, they both create a ContextImpl object during initialization, which implements the methods in the Context
Context key function
Public abstract Class Context {// Get the application package AssetManager instance public abstract AssetManager getAssets(); Public abstract Resources getResources(); Public abstract PackageManager getPackageManager(); public abstract PackageManager getPackageManager(); Public abstract ContentResolver getContentResolver(); Public Abstract Looper getMainLooper(); public abstract Looper getMainLooper(); public abstract Looper getMainLooper(); Public abstract Context getApplicationContext(); Public final String getString(int resId) {returngetResources().getString(resId); } class loader public abstract ClassLoader getClassLoader(); Public Abstract String getPackageName(); Public abstract ApplicationInfo getApplicationInfo(); SharedPreferences Public Abstract SharedPreferences getSharedPreferences(String name, int mode); / / its root directory as follows: Environment. External.getexternalstoragedirectory () public abstract File getExternalFilesDir (Stringtype); Public abstract File getObbDir(); Public void startActivity(Intent Intent, Bundle options); Public void startActivities(Intent[] intents, Bundle options); Public abstract void sendBroadcast(intent intent); public abstract void sendBroadcast(intent intent); Public abstract void sendBroadcast(Intent intent,String receiverPermission); // Broadcast an intent to all interested recipients. // Register a BroadcastReceiver, And it runs public Abstract Intent registerReceiver(BroadcastReceiver Receiver, IntentFilter filter) in the main activity thread. public abstract void unregisterReceiver(BroadcastReceiver receiver); Application Service Public Abstract ComponentName (Intent Service); Public Abstract Boolean stopService(Intent Service); Public abstract Boolean Connects to an application service that defines a dependency between application and servicebindService(Intent service, ServiceConnection conn, int flags); Public abstract void unbindService(ServiceConnection conn) public abstract void unbindService(ServiceConnection conn) Public abstract int checkPermission(String permission, int pid, int uid); }Copy the code
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); /*package*/ LoadedApk mPackageInfo; Private String mBasePackageName; private Resources mResources; /*package*/ ActivityThread mMainThread; // Override public AssetManagergetAssets() {
return getResources().getAssets();
}
@Override
public Looper getMainLooper() {
return mMainThread.getLooper();
}
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options); }}Copy the code
When to create Context
1) When creating an Application object (there is one Application object in the whole App)
2) When creating a Service object
3) When creating an Activity object
When creating application, Service, and Activity objects in ActivityThread, use the activity as an example to start the activity, in addition to using the classLoader to initialize the related objects
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity) cl.loadClass(className).newInstance();
}Copy the code
It also assigns a new context object, and the main code is as follows
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); / / to omitreturn appContext;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
ClassLoader classLoader = packageInfo.getClassLoader();
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}Copy the code
The ApplicationContext is initialized with the following key code
// Each Activity is created first. If the Application has already been generated, Public Application makeApplication(Boolean forceDefaultAppClass, Instrumentation) {if(mApplication ! = null) {returnmApplication; } Application app = null; String appClass = mApplicationInfo.className; // If the application name is not specified in the manifest file, a name is set by defaultif (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if(instrumentation ! = null) { try { instrumentation.callApplicationOnCreate(app); } catch (Exception e) { } }return app;
}Copy the code
Service creates key code
private void handleCreateService(CreateServiceData data) { LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); // Use mClassLoader to load the Service class. And call the structural method to generate the service object service = packageInfo. GetAppFactory () instantiateService (cl, data. Info. Name, data. The intent); } catch (Exception e) { } try { ContextImpl context = ContextImpl.createAppContext(this, packageInfo); Context.setoutercontext (service); / / like the Activity, to determine whether have generated Application Application app = packageInfo. MakeApplication (false, mInstrumentation);
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) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
}
}Copy the code
After source code analysis, we know that the context generated during the creation of the Application, Activity, and Service will eventually be assigned to the mBase in ContextWrapper. In addition, the mBase corresponding to the Application and Service is exactly the same. The mBase corresponding to the Activity is richer, including not only the package information, but also the ActivityInfo (theme, window, start mode, etc.).
GetApplication () == getApplicationContext(), both are equivalent, and both are mApplication instances.
Why does Android provide two methods that duplicate functions? There is a difference in scope. The getApplication() method can only be called in activities and services. GetApplicationContext () belongs to the ContextWrapper method.
An application has several contexts
Number of contexts = Number of Activities + Number of Services +1
The Context scope
A Dialog must pop up on top of an Activity (unless it is a System Alert Dialog).
To sum up: All uI-related activities should be handled using the Activity Context. Some other operation, Service, Activity, Application instance can, of course, pay attention to the Context reference to hold, to prevent a memory leak.
Use Context correctly
Examples of incorrect use:
1. Singleton mode holding
2, static view holding
The memory leak caused by the Context is almost always caused by the destruction of the Context, but the destruction fails because of the reference. The Application Context object can be understood as existing with the process. Therefore, we have concluded the correct position of using the Context: 1: The Application Context is used preferentially when the Application Context can be handled and the object has a long lifetime. 2: Do not allow objects with a lifetime longer than the Activity to hold references to the Activity. 3: Try not to use a non-static inner class in an Activity, because a non-static inner class implicitly holds a reference to an external class instance. If you use a static inner class, the external instance reference is held as a weak reference.
Reference for this article:
Juejin. Cn/post / 684490…