Detailed explanation of Android Context (full analysis)

Posted by danago on Sat, 22 Jan 2022 05:45:30 +0100

The most commonly used activities, services and applications are subclasses of Context. Therefore, it is very necessary to know the specific implementation of Context.
The following is the architecture diagram of Context:

Context itself is an abstract class. His implementation class is ContextImpl. ContextWrapper is a wrapper class (decoration design pattern). When we use the IDE to view the context inheritance relationship, we can't directly see the ContextImpl class. This means that programmers can't directly see the ContextImpl class, but only the ContextWrapper class. But if we really want to understand the principle of context, ContextImpl is the most important, and ContextWrapper is just a shell.

We are very familiar with the subclasses of ContextWrapper, application, service and ContextThemeWrapper. Needless to say more about the first two. In fact, we don't need to care too much about the ContextThemeWrapper. We can see it when necessary. We don't need to say more about its subclass Activity even if it is related to the topic. So the core class is the ContextImpl class.

The Context class is an abstract class with almost 5000 lines, almost all of which are abstract methods without implementation, and almost 4000 lines are comments. The real content may be less than 1000 lines. Therefore, the Context class is almost an interface description document. The ContextImpl is only 2700 lines. And many of them are get methods. The ContextImpl class inherits the abstract class Context and implements all abstract methods. It also implements many methods starting with get, create and check. Even so, it only has 2700 lines of code. To understand Context, the core lies in these 2700 lines of code (many of which are get methods), and there is also the document description class of Context.

In the 2700 lines of code, the most important is ActivityThread mMainThread; This global variable. He is used the most.
The getApplicationContext, startActivity and getMainLooper we often see are implemented here, and the implementation method is also very simple. This is encapsulated by ActivityThread. The specific content depends on the ActivityThread class. Not here, I will write an article for discussion. Most of them use and broadcast light. If you need to study broadcasting, you can have a look.

    @Override
    public Looper getMainLooper() {
        return mMainThread.getLooper();
    }

    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    
    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
    
    //There are almost 10 similar methods
    @Override
    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {

            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                    getUserId());

    }

The second important variable is LoadedApk mpacketinfo, because it is referenced the second most times. You can either get some content through mpacketinfo or make judgments through it. Of course, these functions are encapsulated by the LoadedApk class. You can know what it is through its name. This class can manage and run apk file. The detailed description of this class can also be written in a separate article.

  @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    @Override
    public String getPackageName() {
        if (mPackageInfo != null) {
            return mPackageInfo.getPackageName();
        }
        // No mPackageInfo means this is a Context for the system itself,
        // and this here is its name.
        return "android";
    }
    @Override
    public ApplicationInfo getApplicationInfo() {
        if (mPackageInfo != null) {
            return mPackageInfo.getApplicationInfo();
        }
        throw new RuntimeException("Not supported in system context");
    }

    @Override
    public String getPackageResourcePath() {
        if (mPackageInfo != null) {
            return mPackageInfo.getResDir();
        }
        throw new RuntimeException("Not supported in system context");
    }
    
//There are almost 10
void sendOrderedBroadcast() {
           Multiple use mPackageInfo Make judgment
        }
  
  
    }

Getcontext(), getbasecontext(), getapplication, getapplicationcontext

These are very confusing for novices. What's the difference?

Let's talk about the difference between getApplication and getApplicationContext. If you print the two methods in the Activity, the memory address of the object is the same, that is, the objects obtained by the two methods are the same? In fact, the two obtained objects are the same object. However, the scope of use is different. getApplication is only available for activities and services. If you want to get the Application elsewhere, you can only get it through getApplicationContext.
Here's another question: why do getApplication and getApplicationContext get the same object?
Length reasons. You can read this article as long as you know that the objects obtained by these two methods are the same.
That is, they are almost equivalent. The only difference is whether there is a getApplication method for you.
https://www.cnblogs.com/mingfeng002/p/11995177.html

getContext(),getBaseContxet(),getApplicationContext
What's the difference between the three?
First, Activity,Service and Application all have getbasecontext(), getapplicationcontext(). But there is no getContext method. Only in the Fragment can there be a getContext method. The object obtained by the getContext method of Fragment is its host object, that is, Activity. If you look at the getContext source code, after a series of wrappers, you actually get the Activity object in which it is located.

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d("MainActivity", "getBaseContext():" + getBaseContext());//ContextImpl object
        Log.d("MainActivity", "getApplicationContext():" + getApplicationContext());
        Log.d("MainActivity", "getApplication():" + getApplication());
        Log.d("MainActivity", "this:" + this);//Activity object
    }


public class BlankFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BlankFragment", "getContext():" + getContext());
    }
}

getBaseContext():androidx.appcompat.view.ContextThemeWrapper@72a2522
getApplicationContext():android.app.Application@e8e5bb3
getApplication():android.app.Application@e8e5bb3
this:com.example.viewpagerdemo.MainActivity@b0be2f
getContext():com.example.viewpagerdemo.MainActivity@b0be2f

In the output, we can see that getContext and this of MainActivity are the same object, and getApplicationContext and getApplication get the same object, which also conforms to our proof.

Application usage

First, the global singleton. Therefore, it is unnecessary to implement the singleton by yourself. Just get it through getApplication or getApplicationContext above.
Call the context method in the application after attachBaseContext. Because the assignment of context is completed in attachBaseContext.
For example, the method calling Context in the constructor will collapse because it has not been assigned.
It is possible to call Context in onCreate method, but the limit operation is in attachBaseContext method.

public class MyApplication extends Application {
	
	@Override
	protected void attachBaseContext(Context base) {
		// The method calling Context here will crash
		super.attachBaseContext(base);
		// The Context method can be called normally here
	}
	
}

Topics: Java Android Apache