1. Context

Context is an abstract class. Activity, Service, and Application are subclasses of Context.

From the perspective of the Android system, Context is a scene that describes the information of an application environment. Context represents a process of interaction with the operating system.

Context is an abstract class, and Activity, Service, Application, and so on are implementations of this class.

View class inheritance: CTRL + H (Windows)


Insert a picture description here


An Application creates a Context object in three situations: 1> When an Application object is created, that is, when the app is first launched. The entire App has one Application object, so there is only one Application Context, and when the Application is destroyed, it is destroyed; 2> When creating the Activity object. The Activity is destroyed, and it is destroyed; 3> Create the Service object. The Service is destroyed, and it is destroyed.

The number of contexts that an App can create (activities and services will not be created until they are started) is generally expressed as follows: Total number of Context instances = Number of Services + Number of Activities + 1 (Application Context)

2. Context 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 {
    /**
     * File creation mode: the default mode, where the created file can only
     * be accessed by the calling application (or all applications sharing the
     * same user ID).
     * @see #MODE_WORLD_READABLE
     * @see #MODE_WORLD_WRITEABLE*/ public static final int MODE_PRIVATE = 0x0000; public static final int MODE_WORLD_WRITEABLE = 0x0002; public static final int MODE_APPEND = 0x8000; public static final int MODE_MULTI_PROCESS = 0x0004; ...}Copy the code

Context provides an interface to global information about the application environment. It is an abstract class whose execution is provided by the Android system. It allows access to application-specific resources and types, and is a context governing some resources (application environment variables, etc.). That is, 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). Context is an abstract class, so there must be an implementation of it. We can see its subclasses in the IDE source code, and we can get the following diagram:


Context inheritance structure

3, Context scope

Context is powerful, but you can’t just grab a Context instance and do anything you want. There are rules that limit its use. Since the Context instance is implemented by the ContextImpl class, the Activity, Service, and Application Context types are common in most scenarios. However, there are a few special scenarios, such as starting an Activity and popping up a Dialog. For security reasons, Android does not allow an Activity or Dialog to appear out of thin air, and one Activity must be launched on top of another, creating a return stack. A Dialog must pop up on top of an Activity (unless it’s a System Alert Dialog), so in this scenario we can only use an Activity-type Context, otherwise we’ll get an error.


The Context scope


From the figure above we can see that the Context held by the Activity has the widest scope and can do anything. Since the Activity inherits from ContextThemeWrapper, and the Application and Service inherit from ContextWrapper, ContextThemeWrapper obviously does something on top of ContextWrapper to make the Activity more powerful, so I’m not going to post the source code here. Without further explanation of YES and NO in the figure above, I would like to mention two situations in which Application and Service are not recommended. 1: if we use the ApplicationContext to launch a LaunchMode for standard Activity complains when android. Util. AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? This is because a non-activity Context does not have a task stack, so the Activity to be launched cannot find the stack. The solution to this problem is to specify the FLAG_ACTIVITY_NEW_TASK bit for the Activity to be started, so that a new stack of tasks is created for it when the Activity is started in singleTask mode. This method of starting an Activity with Application is not recommended. Service is the same as Application. Layout constructs are also legal in applications and services, but they use the default theme style, which may not be used if you customize them. Therefore, this method is not recommended. 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.

4 the Context number

So how many contexts are there in an application? We already know the answer based on the Context type above. Context can be an Application, an Activity, or a Service. Context can be an Application, an Activity, or a Service.

Number of contexts = Number of Activities + Number of Services + 1Copy the code

The 1 above represents the number of applications, because an Application can have multiple activities and services, but only one Application.

5 Design of Application Context

Basically, every Application will have its own Application, which inherits from the system’s Application class, and then encapsulates some common operations in its Application class. This is not recommended by Google, because we use Application as a generic utility class, when a simple singleton class can do the same thing. But from my observation, too many projects use Application in this way. Of course, this practice does not have any side effects, but it shows that many people still lack understanding of Application. So here we will first analyze the design of Application, talk about some details that we do not know, and then look at the problems of using Application in daily life.

First create a new MyApplication and make it inherit from Application, then specify MyApplication in the androidmanifest.xml file as follows:

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">... </application>Copy the code

When specified, the Android system will create an instance of MyApplication when our Application starts. If this is not specified, an instance of Application will be created by default.

As mentioned earlier, many applications are now used as a generic utility class, so as a generic utility class, how can we get an instance of it? As follows:

public class MainActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyApplication myApp = (MyApplication) getApplication();
        Log.d("TAG"."getApplication is "+ myApp); }}Copy the code

As you can see, the code is very simple, just call getApplication() to get an instance of our custom Application, and print the result as follows:


Insert a picture description here

So in addition to the getApplication() method, there’s actually a getApplicationContext() method. These two methods seem to be related. What’s the difference? Let’s change the code:

public class MainActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyApplication myApp = (MyApplication) getApplication();
        Log.d("TAG"."getApplication is " + myApp);
        Context appContext = getApplicationContext();
        Log.d("TAG"."getApplicationContext is "+ appContext); }}Copy the code

Again, we printed out the result of getApplicationContext(), now rerunge the code, and the result should look like this:


Insert a picture description here


Yi? It seems that the printed result is the same, even the memory address behind is the same, it seems that they are the same object. This is easy to understand because Application itself is a Context, so the result of getting getApplicationContext() is an instance of MyApplication itself.

So some friends may ask, since the results of both methods are the same, then why does Android provide two methods with the same function? There is actually a big difference in scope between the two methods. The getApplication() method is semantic enough to get an Application instance, but it can only be called in activities and services. If you want to get an instance of an Application in a BroadcastReceiver, you might want to use it in an Activity or a Service for the most part. You can then use the getApplicationContext() method, as follows:

public class MyReceiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        MyApplication myApp = (MyApplication) context.getApplicationContext();
        Log.d("TAG"."myApp is "+ myApp); }}Copy the code

That is, the getApplicationContext() method has a wider scope, and any instance of the Context can get our Application object by calling getApplicationContext().

If you’re more careful, you’ll notice that there’s another method, getBaseContext(). What’s that baseContext? Let’s verify this by printing:


Insert a picture description here


Oh? This time it gets a different object, the getBaseContext() method gets a ContextImpl object. Does this ContextImpl feel a bit familiar? Going back to the Context inheritance diagram, ContextImpl is the implementation class for Context functionality. That is, classes such as Application and Activity do not implement the Context’s functions, but simply encapsulate the interface. The Context’s functions are handled by the ContextImpl class. So how does this design work? Let’s take a look at the source code. ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper: ContextWrapper

/**
 * 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;
    
    /**
     * 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; } / * * * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }
 
    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }
 
    @Override
    public Resources getResources() {
        return mBase.getResources();
    }
 
    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
 
    @Override
    public Looper getMainLooper() {
        return mBase.getMainLooper();
    }
    
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
 
    @Override
    public String getPackageName() {
        return mBase.getPackageName();
    }
 
    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }
    
    @Override
    public void sendBroadcast(Intent intent) {
        mBase.sendBroadcast(intent);
    }
 
    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        return mBase.registerReceiver(receiver, filter);
    }
 
    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        mBase.unregisterReceiver(receiver);
    }
 
    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
 
    @Override
    public boolean stopService(Intent name) {
        return mBase.stopService(name);
    }
 
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
 
    @Override
    public void unbindService(ServiceConnection conn) {
        mBase.unbindService(conn);
    }
 
    @Override
    public Object getSystemService(String name) {
        returnmBase.getSystemService(name); }... }Copy the code

Since there are a lot of methods in ContextWrapper, I did some filtering and only posted some methods. GetResources (), getPackageName(), getSystemService(), etc. What about the implementation of all of these methods? In fact, all ContextWrapper methods are implemented very uniformly, calling the mBase object corresponding to the current method name.

So what is this mBase object? Look at the attachBaseContext() method on line 16, which takes a base argument and assigns it to the mBase object. The attachBaseContext() method is actually called by the system, which passes the ContextImpl object as an argument to the attachBaseContext() method and assigns it to the mBase object. Then all the methods in ContextWrapper are actually implemented by ContextImpl through this delegate mechanism, so it is quite accurate to say that ContextImpl is the implementation class of the context function.

So take another look at the getBaseContext() method we just printed, on line 26. This method has only one line of code and returns the mBase object, which is the ContextImpl object, so the print results are verified.

6 Use Context correctly

The memory leak caused by the Context is almost always when the Context is destroyed, but the destruction fails because it is referenced. 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.

7 summary

Context is very important in The Android system. It can do almost anything, but it is not something you can just use. Beware of memory problems caused by improper use.

Well, that’s the end of the article. If you think it’s good, give it a thumbs up. If you think there is something worth improving, please leave a message. Will inquire seriously, correct inadequacy. thank you

Finally here is about my own Android learning, interview documents, video collection, interested partners can have a look ~

Insert a picture description here


Insert a picture description here


2019Android interview document (including answer analysis)



You can click on:
Github.com/Android-Alv…
To get the