Explanation of Android Application Context and source code analysis
1. Background
Today, I suddenly remembered that when I was in my previous company (for TV and BOX boxes), several people asked me what the Android Context was, so I am about to give birth to this article. We are always using Context when developing an App (not to mention that you have never used Context when accessing resources of the current App or starting an activity ), however, few people have paid attention to the difference between getApplication and getApplicationContext, and the number of contexts in an App.
Even more fatal is that improper use of Context may cause memory leakage. Therefore, it is absolutely necessary to analyze it separately (based on Android 5.1.1 (API 22) source code analysis ).
2 Context Basic Information
2-1 Context Concept
Let's take a look at the basic situation of the Context class of the source code as follows:
/** * 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-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */public abstract class Context { ......}
From the source code annotations, Context provides an interface for global information about the application environment. It is an abstract class and its execution is provided by the Android system. It allows you to obtain resources and types characterized by applications. It is a context that directs resources (application environment variables, etc.
Have you seen the above Class OverView? It describes the information (context) of an application environment. It is an abstract class. Android provides the specific implementation class of this abstract class; with this, we can obtain application resources and classes (including application-level operations, such as starting Activity, broadcasting, and accepting Intent ).
Since Context is an abstract class, there must be its implementation class. In the Context source code, we can use IDE to view its sub-classes as follows:
The first sub-classes, after a rough look at the sub-class names and materials found that these sub-classes are nothing more than the following main inheritance relationships. All these 737 classes are directly or indirectly subclasses of the following relational graph. The main inheritance relationships are as follows:
Here we can find that the class inheritance of Service and Application is similar, and Activity inherits ContextThemeWrapper. This is because the Activity has a topic (the Activity provides UI display, so the topic is required), and the Service is a Service without an interface.
Therefore, we will analyze the source code of Context from this main graph.
2-2 link source code overview between Context
With the above general relationships and graphs viewed through IDE, we will take a closer look at these inheritance relationships in the source code.
Let's take a look at the source code comment of the Context class:
/** * 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-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */public abstract class Context { ......}
The abstract class Context provides a set of common APIs.
Let's take a look at the ContextImpl source code annotation of the Context implementation class:
/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */class ContextImpl extends Context { private Context mOuterContext; ......}
This class implements all the functions of the Context class.
Let's take a look at the ContextWrapper source code annotation of the Context packaging class:
/** * Proxying implementation of Context that simply delegates all of its calls to * another Context. Can be subclassed to modify behavior without changing * the original Context. */public class ContextWrapper extends Context { 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; } ......}
The ContextImpl object contains a real Context reference, and then becomes the ContextImpl decoration mode.
Let's take a look at the ContextWrapper subclass ContextThemeWrapper source code comment:
/** * A ContextWrapper that allows you to modify the theme from what is in the * wrapped context. */public class ContextThemeWrapper extends ContextWrapper { ......}
This class contains Theme-related interfaces, that is, the interfaces specified by the android: theme attribute.
Let's take a look at the inheritance relationship source code of Activity, Service, and Application classes:
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { ......}
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { ......}
public class Application extends ContextWrapper implements ComponentCallbacks2 { ......}
See no? They fully match the structure chart and overview we have drawn above.
2-3 solve questions about the number of application Context
With the above Context inheritance relationship Verification and analysis, let's see if the next application has multiple Context?
Android applications have only four components, both of which are inherited from Context, and each Application has a global Application object. So after we understand the inheritance relationship above, we can calculate the total number of Context, as shown below:
Total APP Context = number of applications (1) + number of activities (Customer) + number of services (Customer );
At this point, we have also clarified what Context is, what the inheritance relationship is, and what the number of Context is in the application. Next, it is necessary to analyze in depth how these Context come from.
[Craftsman if water http://blog.csdn.net/yanbober reprinted please indicate the source, respect sharing results]
3. source code analysis of various Context instantiation processes in ActivityThread
Before starting the analysis, we will give the following content in sections 3-1-2 of "Explanation of Android Asynchronous Message Processing Mechanism and source code analysis" and 2-6 of "Android app setContentView and LayoutInflater loading parsing mechanism source code analysis ". some concepts about Activity startup, I will write an article later to analyze this process.
The implementation of Context is ContextImpl, and the creation of Activity, Application, and Service is completed in ActivityThread. As for the relationship between ActivityThread and how to dispatch it, I will write an article for analysis. Here I will directly draw a conclusion, because the focus of our analysis is the Context process.
3-1 ContextImpl instantiation source code analysis in Activity
When a new Activity is started through startActivity, the system calls back the handleLaunchActivity () method of ActivityThread. The method internally calls the initiate mlaunchactivity () method to create an Activity instance, and then calls back the onCreate () method of the Activity (). Therefore, the ContextImpl instantiation of the Activity is in the javasmlaunchactivity method of the ActivityThread class, as follows:
Private Activity extends mlaunchactivity (ActivityClientRecord r, Intent customIntent) {... // a new activity instance has been created if (activity! = Null) {// create a Context object Context appContext = createBaseContextForActivity (r, activity );...... // pass the created appContext to the attach method activity of the 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 );......}...... return activity ;}
Have you seen the above javasmlaunchactivity's core code? PasscreateBaseContextForActivity(r, activity);
Create an appContext and set the value through activity. attach.
Let's take a look at the source code of the createBaseContextForActivity method, as shown below:
Private Context createBaseContextForActivity (ActivityClientRecord r, final Activity activity) {// It is essentially a new ContextImpl object. It is called to construct a ContextImpl parameter with parameters to initialize ContextImpl appContext = ContextImpl. createActivityContext (this, r. packageInfo, r. token); // pay special attention to this !!! // In ContextImpl, a Context Member is called mOuterContext. With this statement, the current new Activity object can be assigned to the created ContextImpl member mOuterContext (that is, let ContextImpl hold the Activity internally ). AppContext. setOuterContext (activity); // create the returned value and assign the value Context baseContext = appContext; ...... // return the ContextImpl object return baseContext ;}
Let's take a look at activity. attach, that is, the attach method in Activity, as follows:
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 failed, configuration config, String referrer, IVoiceInteractor voiceInteractor) {// pay special attention to this !!! // Similar to the setOuterContext statement in the createBaseContextForActivity method, the difference is: // pass the instantiated ContextImpl object in createBaseContextForActivity to the mBase variable of the ContextWrapper class through the attachBaseContext method of the lifecycle class, in this way, the member mBase of the ContextWrapper (Context subclass) class is instantiated as the ContextImpl attachBaseContext (Context );......}
We can see from the Context instantiation analysis of the above Activity and the above Context inheritance relationship:
Activity uses the ContextWrapper member mBase to reference A ContextImpl object. In this way, the Activity component can then use this ContextImpl object to execute specific operations (such as starting the Service ); at the same time, the ContextImpl class references the Activity associated with it through its own member mOuterContext, so that the ContextImpl class can also operate the Activity.
SO, it indicates that an Activity has a Context, and the lifecycle is the same as that of the Activity class (remember this sentence and write the application to avoid some low-level memory leakage issues ).
3-2 Service ContextImpl instantiation source code analysis
When we create a new Service using the startService or bindService method when writing an APP, The handleCreateService () of the ActivityThread class will be called back () method to complete the relevant data operations (the detailed analysis of the time when ActivityThread transfers handleCreateService is the same as the above Activity, which will be analyzed later ). The specific handleCreateService method code is as follows:
Private void handleCreateService (CreateServiceData data ){...... // similar to the above Activity creation, here the service object instance Service = null is created; try {java. lang. classLoader cl = packageInfo. getClassLoader (); service = (Service) cl. loadClass (data.info. name ). newInstance ();} catch (Exception e ){......} try {...... // create a ContextImpl Context = ContextImpl. createAppContext (this, packageInfo); // pay special attention to this !!! // In ContextImpl, a Context Member is called mOuterContext. With this statement, the current new Service object can be assigned to the created ContextImpl member mOuterContext (that is, to allow ContextImpl to hold the Service internally ). Context. setOuterContext (service); Application app = packageInfo. makeApplication (false, mInstrumentation); // pass the context created above to the attach method service of the service. attach (context, this, data.info. name, data. token, app, ActivityManagerNative. getDefault (); service. onCreate ();......} catch (Exception e ){......}}
Let's take a look at service. attach, that is, the attach method in Service, as follows:
Public final void attach (Context context, ActivityThread thread, String className, IBinder token, Application application, Object activityManager) {// pay special attention to this !!! // Similar to the setOuterContext statement in the preceding terminate method, the difference is: // pass the instantiated ContextImpl object in handleCreateService to the mBase variable of the ContextWrapper class through the attachBaseContext method of the ContextWrapper class, in this way, the member mBase of the ContextWrapper (Context subclass) class is instantiated as the ContextImpl attachBaseContext (Context );......}
We can see that the process is similar to the Activity, but the implementation details are slightly different.
SO, it indicates that a Service has a Context, and the lifecycle is the same as that of the Service class (remember this sentence and write the application to avoid some low-level memory leakage issues ).
3-3 ContextImpl instantiation source code analysis in Application
After an APP is written, the Application object will be created every time it is restarted (each APP has a unique global Application object, which is the same as the entire APP lifecycle ). The process of creating an Application also completes related data operations in the handleBindApplication () method of the ActivityThread class (the detailed analysis on the timing of ActivityThread's handleBindApplication scheduling is the same as that of the above Activity, and will be analyzed later ). ContextImpl is created in this method by calling the makeApplication method of the LoadedApk class. The source code of the makeApplication () method of the LoadedApk class is as follows:
Public Application makeApplication (boolean forceDefaultAppClass, Instrumentation instrumentation) {// only newly created apps can use the remaining logic if (mApplication! = Null) {return mApplication;} // Application object to be created Application app = null; String appClass = mApplicationInfo. className; if (forceDefaultAppClass | (appClass = null) {appClass = android. app. application;} try {java. lang. classLoader cl = getClassLoader (); if (! MPackageName. equals (android) {initializeJavaContextClassLoader ();} // create a Context object ContextImpl appContext = ContextImpl without too much explanation. createAppContext (mActivityThread, this); // pass Context to the newApplication method app = mActivityThread of the Instrumentation class. mInstrumentation. newApplication (cl, appClass, appContext); // pay special attention to this !!! // In ContextImpl, a Context Member is called mOuterContext. With this statement, the new Application object can be assigned to the created ContextImpl member mOuterContext (that is, let ContextImpl hold the Application internally ). AppContext. setOuterContext (app);} catch (Exception e) {...} ...... return app ;}
Next let's take a look at the Instrumentation. newApplication method. Source code:
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return newApplication(cl.loadClass(className), context); }
Continue to see the newApplication Method for reloading two parameters, as shown below:
Static public Application newApplication (Class
Clazz, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {... // continue to pass context app. attach (context); return app ;}
Continue to check the attach method of the Application class as follows:
Final void attach (Context context) {// pay special attention to this !!! // Similar to the setOuterContext statement in the makeApplication method above, the difference is: // pass the attachBaseContext method of the ContextWrapper class to pass the instantiated ContextImpl object in the makeApplication to the mBase variable of the ContextWrapper class, in this way, the member mBase of the ContextWrapper (Context subclass) class is instantiated as the implementation class ContextImpl attachBaseContext (context); ......} of the Application );......}
We can see that the process is similar to the Activity, but the implementation details are slightly different.
SO, it indicates that an Application has a Context, and the lifecycle is the same as that of the Application class (however, an App only has one Application, and the lifecycle is the same as that of the Application ).
4. uniqueness analysis of various Context access resources of the application APP
You may have doubts that so many Context instances are different. Do we usually get resources through context. getResources when we write an App? We will analyze the source code as follows:
class ContextImpl extends Context { ...... private final ResourcesManager mResourcesManager; private final Resources mResources; ...... @Override public Resources getResources() { return mResources; } ......}
No. With the above analysis, we can determine that the Resources object obtained by the context. getResources method in the App is the member variable mResources of ContextImpl above. We can find that the assignment operation of mResources is as follows:
Private ContextImpl (ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration ){...... // obtain the ResourcesManager object in singleton mode: mResourcesManager = ResourcesManager. getInstance ();...... // packageInfo has only one APP, so resources are the same. Resources = packageInfo. getResources (mainThre Ad); if (resources! = Null) {if (activityToken! = Null | displayId! = Display. DEFAULT_DISPLAY | overrideConfiguration! = Null | (compatInfo! = Null & compatInfo. applicationScale! = Resources. getCompatibilityInfo (). applicationScale) {// mResourcesManager is a Singleton, so resources are the same resource = mResourcesManager. getTopLevelResources (packageInfo. getResDir (), packageInfo. getSplitResDirs (), packageInfo. getOverlayDirs (), packageInfo. getApplicationInfo (). sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, activityToken);} // assign resources to mResources = resources ;......}
We can see that the Resources we get from different Context instances are the same set of Resources without changing other factors on the device.
PS, the same analysis method can also find that the packageInfo of the Context class is only one copy for one application. If you are interested, you can analyze it yourself.
5. Use different source code analysis for various APP Context
5-1 first solve the difference between getApplication and getApplicationContext
Many people have always been unable to distinguish between the two methods. Here we will analyze the source code as follows:
First, let's take a look at the getApplication method. You will find that neither Application nor Context provides this method. Which method does this provide? Let's take a look at the code in Activity and Service. You can send it as follows:
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { ...... public final Application getApplication() { return mApplication; } ......}
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { ...... public final Application getApplication() { return mApplication; } ......}
Activity and Service provide getApplication, And the return type is Application. This mApplication comes in and out of the attach method parameters of each class. That is to say, this mApplication is the makeApplication method return value obtained during each instantiation in ActivityThread.
Therefore, the applications returned by different activities and services are the same global object.
Let's take a look at the getApplicationContext method as follows:
class ContextImpl extends Context { ...... @Override public Context getApplicationContext() { return (mPackageInfo != null) ? mPackageInfo.getApplication() : mMainThread.getApplication(); } ......}
We can see that the getApplicationContext method is the Context method, and the returned value is the Context type. The returned object is an object that is returned through the getApplication of Service or Activity.
Therefore, for custom third-party applications, the return values of the two methods are the same, but the return values are of different types, and the attached objects are different.
5-2 differences in various methods for obtaining Context and tips for development
As you can see, the Context life cycle of the Application is exactly the same as that of the Application.
The Context of the Activity or Service is the same as the life cycle of their respective classes.
Therefore, improper use of Context may cause memory leakage.
For example, a single-instance Mode Custom database management tool class needs to input a Context, and this database management object needs to be used in the Activity. If we pass the Context of the Activity, memory leakage may occur, therefore, the Context of the Application must be passed.
6 Context analysis summary
By now, the Context of the entire Android application has been completely uncertain. Based on the source code analysis results, it also provides tips and solutions for Memory leakage issues that should be noted during APP development. I believe that you will not be confused with the use of Context when developing apps through this article.