In-depth analysis of the use of context in Android app development _android

Source: Internet
Author: User
Tags wrapper

The context plays a very important role in the development of Android apps, Like starting an activity. You need to use the Context.startactivity method to convert an XML file to a view object, and you need to use the context object, so to speak, if you leave this class, Android is nowhere to go, how much do we know about such a class? 。 I will tell you how I feel, in the beginning to learn the development of Android, feel the use of the context of the place has been the introduction of an Activity object, over time feel as long as the context of the place on the introduction of an activity on the line, So let's analyze the relationship between the context and the activity in detail now.
Before we begin this article, let's put a question here:
We usually get the project resources when using context.getresources () Why put back the same value, obviously using a different activity call getresources return result is the same.

The context itself is a pure abstract class, Contextwrapper is a wrapper over the context, and its interior contains a context object, In fact, the Contextwrapper method call is ultimately called the context object to complete, as for Contextthremewrapper, obviously and theme, So the activity inherits from the Contextthemmwrapper, and the service inherits from Contextwrapper, Contextimpl is the only class that truly implements the method in the context.

From the above inheritance relationship, each activity is a context, each service is a situation, which is why the use of the context of the place can be activity or service replacement.

Create context
According to the foregoing, because only the Contextimpl class is implemented in the context, the activity and service have not really been implemented, they only contain a real context object internally. That is, when creating an activity or service, be sure to create love you a Contextimpl object and assign it to the context type variable in the activity. Let's take a look at where the Andorid source code has created Contextimpl.
According to statistics, there are 7 sites in Android that create Contextimpl:

    • In Packageinfo.makeapplication ()
    • In Performlaunchactivity ()
    • In Handlecreatebackupagent ()
    • In Handlecreateservice ()
    • 2 times in Hanldbinderappplication ()
    • In the Attach () method


Because the fundamentals of creating Contextimpl are similar, there are only a few more representative areas to analyze:
1, application corresponding context
when the application starts, a Application object is created, so the call to the Handlebindapplication () method is made.

Private final void Handlebindapplication (Appbinddata data) { 
    mboundapplication = data; 
    Mconfiguration = new Configuration (data.config); 
 
    .... 
    Data.info = Getpackageinfonocheck (data.appinfo); 
 
      ... 
     
    Application app = Data.info.makeApplication (Data.restrictedbackupmode, null); 
    minitialapplication = app; 
 
   .... 
 } 

Where Data.info is the loadedapk type, to the Getpackageinfonocheck to see the source

Public final loadedapk Getpackageinfonocheck (ApplicationInfo ai) {return 
    getpackageinfo (AI, NULL, FALSE, True); c13/>} 

Inside actually call is Getpackageinfo, continue to follow up:

if (includecode) {ref = Mpackages.get (Ainfo.packagename); 
      else {ref = Mresourcepackages.get (Ainfo.packagename); } loadedapk PackageInfo = ref!= null? 
      Ref.get (): null; if (PackageInfo = null | | (packageinfo.mresources!= null &&!packageinfo.mresources.getassets (). IsUpToDate ())) {if (LOCALLOGV) slog.v (TAG, Includecode?) "Loading Code Package": "Loading resource-only Package") + Ainfo.packagename + "(in" + (MBo Undapplication!= null? 
        MBoundApplication.processName:null) + ")"); PackageInfo = new loadedapk (this, ainfo, this, Baseloader, securityviolation, Includecode && 
              Amp 
(Ainfo.flags&applicationinfo.flag_has_code)!= 0); if (Includecode) {mpackages.put (Ainfo.packagename, New weakreference<loadedapk> (Packagein 
        FO)); else {MresourCepackages.put (Ainfo.packagename, New weakreference<loadedapk> (PackageInfo)); 
 }

Since the Includecode passed in is true, first get the mpackages, if not, the new one out and into the mpackages, note that the mpackages here is the attribute in the Activitythread.
Let's continue to analyze the Makeapplication function in the loadedapk class

try { 
      Java.lang.ClassLoader cl = getClassLoader (); 
      Create a Contextimpl object 
      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); 
      } 
     

This creates a Contextimpl object and invokes its Init method, and now enters the Init method.

Mpackageinfo = PackageInfo; 
Mresources = Mpackageinfo.getresources (Mainthread); 

Initialize two variables for mpackageinof and mresources
Back in Makeapplication, a Application object is created and the Appcontext is passed in, which is actually passing appcontext to the context type variable in the Contextwrapper ( Application also inherits Contextwrapper)
2, the context of activity
when an activity is created, it is called by the handlelaunchactivity (), and then the Performlaunchactivity () is invoked, and the method creates the Contextimpl code as follows:

r.packageinfo= Getpackageinfo (Ainfo.applicationinfo,
          context.context_include_code);
 
Contextimplappcontext = new Contextimpl ();
        Appcontext.init (R.packageinfo,r.token, this);
        Appcontext.setoutercontext (activity);
 
Activity.attach (Appcontext,this, Getinstrumentation (), R.token,
            r.ident, app, R.intent,r.activityinfo, title, R.parent,
            r.embeddedid,r.lastnonconfigurationinstance,
            r.lastnonconfigurationchildinstances, config);

Since the Getpackageinfo function has been analyzed before, slightly different, but the approximate process is similar, so the appcontext after Init, where the mpackages variable and the mresources variable, The activity assigns the Appcontext to the context type variable in Contextwrapper through the Attach function.

3, service in the context
also in the creation of a service, after the call will be called to the Schedulecreateservice method, after the use of Handlecreateservice

Loadedapkpackageinfo = Getpackageinfonocheck (
        data.info.applicationInfo);
 
Contextimplcontext = new Contextimpl ();
      Context.init (PackageInfo, null,this);
 
      Application app =packageinfo.makeapplication (false, minstrumentation);
      Context.setoutercontext (service);
      Service.attach (context, This,data.info.name, Data.token, app,
          Activitymanagernative.getdefault ());

The idea is essentially the same as the two above, no longer detailed here.

Context Access to resources
It is clear that the different context will receive the same resources. This is very well understood, please look at the following analysis

The way to get the resources is context.getresources, and the true implementation of the Getresources method in Contextimpl, there is a member of the Contextimpl in the private resource mresources, It is the result of the Getresources method return, and the assignment code for the mresources is:

Mresources = Mresourcesmanager.gettoplevelresources (Mpackageinfo.getresdir (),
          display.default_display, NULL, Compatinfo, Activitytoken);

Look at the Gettoplevelresources method of Resourcesmanager, the idea of this method is that in Resourcesmanager, all resource objects are stored in Arraymap, First, according to the current request parameters to find resources, if found on the return, otherwise create a resource object into the Arraymap. One thing to explain is why there are multiple resource objects, because there may be multiple directories in the res that fit different devices, different resolutions, different system versions, and according to the design of the Android system, different devices can access different resources when accessing the same application. For example, drawable-hdpi and drawable-xhdpi are typical examples.

Public Gettoplevelresources (String resdir, int Displayid, Configuration overrideconfiguration, Compatibili 
  Tyinfo Compatinfo, IBinder token) {final float scale = Compatinfo.applicationscale; 
  Resourceskey key = new Resourceskey (Resdir, Displayid, overrideconfiguration, scale, token); 
  Resources R; 
    Synchronized (this) {//The resources are 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 resource 
      S "+ 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 away mactiveresources.put (key, New Weakreference<re 
    Sources> (R)); 
  return R; 
 } 
}

According to the request mechanism of the resources in the above code, plus resourcesmanager using the single case mode, this ensures that the different Contextimpl access to the same set of resources, note that the same set of resources is not necessarily the same resource, because the resources may be in different directories, But it must be the resource of our application, perhaps this is more accurate description, in the case of device parameters and display parameters unchanged, different Contextimpl access to the same resource. Device parameters unchanged refers to the phone's screen and Android version of the same, the display parameters are the same as the resolution of the phone and the screen state. In other words, although application, activity, service have their own contextimpl, and each contextimpl has its own mresources members, But since their mresources members come from a unique Resourcesmanager instance, their seemingly different mresources actually point to the same memory (the concept of the C language), so Their mresources are all the same objects (in the case of device parameters and display parameters unchanged). In the case of vertical and horizontal screen switching and in the application of the same screen state provides a different resources, in the Contextimpl and in the screen state of the Contextimpl access resources are not the same resource object.

Code: Resourcesmanager class for single case mode

public static Resourcesmanager getinstance () { 
  synchronized (resourcesmanager.class) { 
    if (Sresourcesmanager = = null) { 
      Sresourcesmanager = new Resourcesmanager (); 
    } 
    Return Sresourcesmanager 
  } 
} 

The difference between getapplication and Getapplicationcontext

Getapplication Returns the result is application, and the application returned by different activity and service are the same global object. Within the Activitythread there is a list dedicated to maintaining all applications application

  Final arraylist<application> mallapplications = new arraylist<application> ();

Getapplicationcontext return is also a Application object, except that the return type is context, to see its implementation

@Override Public Context 
Getapplicationcontext () {return 
  (mpackageinfo!= null)? 
      Mpackageinfo.getapplication (): Mmainthread.getapplication (); 
} 

In the code above, Mpackageinfo is the package information that contains the current application, such as package name, application installation directory, etc., in principle, as a third party application, package information Mpackageinfo cannot be empty, in this case, Getapplicationcontext returns objects and Getapplication are the same. But for the system application, the packet information may be empty, the concrete is not further studied. From this perspective, for Third-party applications, an application only has one application object, and the same object is obtained through getapplication and Getapplicationcontext, and the difference is simply the return type.


Here's a summary:
(1) The context is an abstract class, and Contextwrapper is a wrapper over it that contains a variable of the context type, The function inside the contextwrapper is actually called the context type variable inside. Application,service,activity, etc. are directly or indirectly inherited from Contextwrapper, but there is no real implementation of the function, Application,service, The function of the context in the activity is done through its internal context type variable, and the real object of the variable must be Contextimpl, so no application,activity is created, Servcice creates a Contextimpl, and the mpackages and mresources variables are the same in these Contextimpl, So whether you use Acitivty or service call getresources get the same result
(2) in a apk, the number of context equals activity number +service number +1.

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.