Android source code analysis-comprehensive understanding of Context

Source: Internet
Author: User

Reprinted please indicate the source: http://blog.csdn.net/singwhatiwanna/article/details/21829971 (from singwhatiwanna's blog)

Preface

The role of Context in android is self-evident. When we access the resources of the current application and start a new activity, we need to provide Context. What is the Context, this question seems to be a good answer and hard to clarify. Literally, Context means "Context", or it can also be called environment or scenario. However, it is a little abstract. In terms of class inheritance, Context is an abstract base class, and its implementation subclass has three types: Application, Activity, and Service (for the moment, no matter the ContextWrapper class ), are there any differences between the three types? Why is the same set of resources obtained by accessing resources through any Context? What is the difference between getApplication and getApplicationContext? How many contexts are there in an application? This article will focus on these issues. The source code version is Android4.4.

What is Context

Context is an abstract base class. We use it to access the resources (getResources and getAssets) of the current package, start other components (Activity, Service, Broadcast), and get various services (getSystemService ), of course, only the above content can be obtained through Context. To understand the Context, Context provides an application running environment. In the Context environment, an application can access resources to complete interaction with other components and services, context defines a set of basic functional interfaces. We can understand it as a set of standards, and Activity and Service are subclasses that implement this set of standards. This may not be accurate, because this set of specifications is actually implemented by the ContextImpl class in a unified manner, the Activity and Service only inherit and selectively override the implementation of some specifications.

Differences between Application, Activity, and Service as Context

First, they all indirectly inherit the Context, which is the same point between them.

Differences: first, we can look at their inheritance relationships.

Inheritance relationship of Activity


Inheritance relationship between Service and Application


Through comparison, we can clearly find that the class inheritance relationship between Service and Application is similar, while Activity has another level of inheritance ContextThemeWrapper, because Activity has a topic concept, the Service is a Service without interface, and the Application is an abstract thing. It is also presented through the Activity class.

Next, let's take a look at the differences between the three in Context.

As mentioned above, the true implementation of Context is in ContextImpl. That is to say, most of the method calls of Context are transferred to ContextImpl, and the three are created in ActivityThread, I have previously written an article about the Android source code analysis-Activity startup process. In this article, I pointed out that the core process of Activity startup is completed in ActivityThread. Here I want to explain that, application and Service creation is also completed in ActivityThread. The following describes how the three are associated with ContextImpl during creation.

Create ContextImpl in Activity object

The Code is the rabbitmlaunchactivity method in ActivityThread.

If (activity! = Null) {Context appContext = createBaseContextForActivity (r, activity);/*** create ContextImpl code in createBaseContextForActivity * ContextImpl appContext = new ContextImpl (); * appContext. init (r. packageInfo, r. token, this); * appContext. setOuterContext (activity); */CharSequence title = r. activityInfo. loadLabel (appContext. getPackageManager (); Configuration config = new Configuration (mCompatConfiguration) ; If (DEBUG_CONFIGURATION) Slog. v (TAG, "Launching activity" + r. activityInfo. name + "with config" + config); activity. attach (appContext, this, getInstrumentation (), r. token, r. ident, app, r. intent, r. activityInfo, title, r. parent, r. embeddedID, r. lastNonConfigurationInstances, config); if (customIntent! = Null) {activity. mIntent = customIntent ;}...}
It can be seen that the Activity will create a new ContextImpl object and associate it in the attach method. Note that the data structure used to create the Activity is ActivityClientRecord.

Create ContextImpl in Application Object

The code is in the handleBindApplication method in ActivityThread. The makeApplication method is called internally.

public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {if (mApplication != null) {return mApplication;}Application app = null;String appClass = mApplicationInfo.className;if (forceDefaultAppClass || (appClass == null)) {appClass = "android.app.Application";}try {java.lang.ClassLoader cl = getClassLoader();ContextImpl appContext = new ContextImpl();appContext.init(this, null, mActivityThread);app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);appContext.setOuterContext(app);} catch (Exception e) {if (!mActivityThread.mInstrumentation.onException(app, e)) {throw new RuntimeException("Unable to instantiate application " + appClass+ ": " + e.toString(), e);}}...}
The Code shows that ContextImpl is the same as ContextImpl in Activity.

Create ContextImpl in Service object

By viewing the code, we can see that it is consistent with Activity and Application. After analysis, what is the difference between Context and Context? Is there no difference? However, some details are certain: the use of Dialog requires Activity. On the desktop, the Context of the Application cannot pop up the Dialog box. At the same time, you want to start a new activity on the desktop, we need to set the FLAG_ACTIVITY_NEW_TASK flag for intent. Otherwise, the activity cannot be started. All this shows that at least the Context of the Application is different from the Context of the Activity. Of course, this may not be the difference between Context, because there is no interface for our applications on the desktop, which means that we may be limited in terms of what we can do. I have not yet made it very clear.

Context access to resources

It is clear that different contexts get the same resource. This is easy to understand. Please refer to the following analysis.

The method to obtain the resource is context. the real implementation of getResources is located in the getResources method of ContextImpl. In ContextImpl, there is a member private Resources mResources, Which is the result returned by the getResources method. The mResources assignment code is:

MResources = mResourcesManager. getTopLevelResources (mPackageInfo. getResDir (),
Display. DEFAULT_DISPLAY, null, compatInfo, activityToken );

Let's take a look at the getTopLevelResources method of ResourcesManager. The idea of this method is as follows: In ResourcesManager, all resource objects are stored in ArrayMap, first, search for resources based on the current request parameters. If yes, return. Otherwise, create a resource object and put it in ArrayMap. One thing to note is why there are multiple resource objects. The reason is very simple, because there may be multiple directories in res that adapt to different devices, different resolutions, and different system versions, according to the android system design, different devices can access different resources when accessing the same application. For example, drawable-hdpi and drawable-xhdpi are typical examples.

public Resources getTopLevelResources(String resDir, int displayId,Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {final float scale = compatInfo.applicationScale;ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,token);Resources r;synchronized (this) {// Resources is app scale dependent.if (false) {Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);}WeakReference<Resources> wr = mActiveResources.get(key);r = wr != null ? wr.get() : null;//if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());if (r != null && r.getAssets().isUpToDate()) {if (false) {Slog.w(TAG, "Returning cached resources " + r + " " + resDir+ ": appScale=" + r.getCompatibilityInfo().applicationScale);}return r;}}//if (r != null) {//    Slog.w(TAG, "Throwing away out-of-date resources!!!! "//            + r + " " + resDir);//}AssetManager assets = new AssetManager();if (assets.addAssetPath(resDir) == 0) {return null;}//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);DisplayMetrics dm = getDisplayMetricsLocked(displayId);Configuration config;boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);final boolean hasOverrideConfig = key.hasOverrideConfiguration();if (!isDefaultDisplay || hasOverrideConfig) {config = new Configuration(getConfiguration());if (!isDefaultDisplay) {applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);}if (hasOverrideConfig) {config.updateFrom(key.mOverrideConfiguration);}} else {config = getConfiguration();}r = new Resources(assets, dm, config, compatInfo, token);if (false) {Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "+ r.getConfiguration() + " appScale="+ r.getCompatibilityInfo().applicationScale);}synchronized (this) {WeakReference<Resources> wr = mActiveResources.get(key);Resources existing = wr != null ? wr.get() : null;if (existing != null && existing.getAssets().isUpToDate()) {// Someone else already created the resources while we were// unlocked; go ahead and use theirs.r.getAssets().close();return existing;}// XXX need to remove entries when weak references go awaymActiveResources.put(key, new WeakReference<Resources>(r));return r;}}
According to the resource request mechanism in the above Code, coupled with the ResourcesManager single-instance mode, this ensures that different ContextImpl accesses the same set of resources. Note, the same set of resources mentioned here may not be the same resource, because the resource may be located in different directories, but it must be the resource of our application. This may make the description more accurate, when the device parameters and display parameters remain unchanged, different ContextImpl accesses the same resource. If the device parameters remain unchanged, the screen and android version of the mobile phone remain unchanged. The display parameters remain unchanged, indicating the resolution and portrait status of the mobile phone. That is to say, although the Application, Activity, and Service all have their own ContextImpl, and each ContextImpl has its own mResources member, since their mResources members are from the only ResourcesManager instance, therefore, different mResources actually point to the same memory (the concept of C language). Therefore, their mResources are the same object (when the device parameters and display parameters remain unchanged ). Different resources are provided for the landscape screen status in the application when the landscape screen is switched, the resources accessed by ContextImpl and ContextImpl in the landscape are not the same resource object.

Code: single-instance mode ResourcesManager class

    public static ResourcesManager getInstance() {        synchronized (ResourcesManager.class) {            if (sResourcesManager == null) {                sResourcesManager = new ResourcesManager();            }            return sResourcesManager;        }    }
Difference between getApplication and getApplicationContext

The getApplication returns an Application, and the applications returned by different activities and services are the same global object. In ActivityThread, there is a list of applications dedicated to maintaining all applications:

Final ArrayList <Application> mAllApplications = new ArrayList <Application> ()

Why do getApplication return the same Application object because the Application objects returned by the getApplication of Activity and Service are passed to them through their attach method when ActivityThread creates them, that is to say, all applications held by Activity and Service are internal applications of ActivityThread. Because an Application only has one package information, only one Application can be created in ActivityThread because packageInfo is executed. if you have already created an Application during makeApplication, packageInfo. the makeApplication method will not create a new Application. There is only one package for an application. From the logic of the Code, this is true. Inside ActivityThread, there is also a list dedicated to maintaining the package information for all applications:

Final ArrayMap <String, WeakReference <LoadedApk> mPackages = new ArrayMap <String, WeakReference <LoadedApk> ()

GetApplicationContext also returns the Application object, but the return type is Context. Let's see its implementation.

    @Override    public Context getApplicationContext() {        return (mPackageInfo != null) ?                mPackageInfo.getApplication() : mMainThread.getApplication();    }
In the above Code, mPackageInfo contains the package information of the current application, such as the package name and installation directory of the application. In principle, as a third-party application, the package information mPackageInfo cannot be blank, in this case, the object returned by getApplicationContext is the same as that returned by getApplication. However, for system applications, the package information may be empty, so we will not study it further. From this perspective, for a third-party Application, an Application only has one Application object, and the same object is obtained through getApplication and getApplicationContext. The difference between the two is that the return type is different.
The number of Context in the Application has reached this point. The number of Context in an Application is equal to the number of activities + the number of services + 1. This 1 is the Application.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.