The relevant content of the article has been authorized to “Guo Lin” public account published

preface

This is the third and final part of the Context series. We’ve talked about what a context is and how to create a context. Limited by space, the context retrieval process of the broadcast and content provider components has been put into this article. Broadcast and content providers are not part of the Context family, so they are not themselves a context, so their context must be directly or indirectly retrieved from Application, Activity, or Service. Then it discusses the design of context. Looking at context from a higher perspective can help us see the essence of context, and also help us better understand and use context.

This is the third part of the Context series, which is divided into three parts: Part 1: What is context and the context family and related implementation classes. Part 2: The creation process of different contexts, including Application, Activity, and Service. Part 3: The final chapter explains the context sources of the remaining two major components and how to understand context from a higher perspective.

Readers can go to the home page to select the sections they are interested in, or to read the entire systematic learning context.

Obtain the Broadcast context

Broadcast, unlike the preceding components, does not inherit from the Context, so its Context needs to be given by Application, Activity, or Service. We usually use the broadcast context in the receiver, as in:

class MyClass :BroadcastReceiver() {
    override fun onReceive(context: Context? , intent:Intent?). {
        TODO("use context")
    }
}
Copy the code

So where does the onReceive context object come from? Also let’s look at the registration process of broadcast receivers:

Also, for details on the Broadcast workflow, see the Android Broadcast registration and Broadcast source code process (based on API29). Since the context is not passed in when the Receiver is created, we need to trace its registration process to see where the context was obtained. ContextImpl’s registerReceiver method:

ContextImpl.class(api29)
public Intent registerReceiver(BroadcastReceiver receiver.IntentFilter filter.        String broadcastPermission.Handler scheduler) {
    // Pay attention to parameters
    return registerReceiverInternal(receiver, getUserId(),
 filter, broadcastPermission, scheduler, getOuterContext(), 0); } Copy the code

The registerReceiver method will eventually come to this overloaded method, and notice that there’s a getOuterContext, what is this? Remember the context creation process for your Activity? This method gets the activity itself. Here we go:

ContextImpl.class(api29)
private Intent registerReceiverInternal(BroadcastReceiver receiver.int userId.        IntentFilter filter.String broadcastPermission.        Handler scheduler.Context context.int flags) {
    IIntentReceiver rd = null;
 if(receiver ! =null) {  if(mPackageInfo ! =null&& context ! =null) { . rd = mPackageInfo.getReceiverDispatcher(  receiver, context, scheduler,  mMainThread.getInstrumentation(), true);  } . } .} Copy the code

ReceiverDispatcher is created using the context. Let’s take a closer look:

LoadedApk.class(api29)
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r.        Context context.Handler handler.        Instrumentation instrumentation.boolean registered) {
    synchronized (mReceivers) {
 LoadedApk.ReceiverDispatcher rd = null; . if (rd == null) {  rd = new ReceiverDispatcher(r, context, handler,  instrumentation, registered); . } . } }  ReceiverDispatcher.class(api29) ReceiverDispatcher(... .Context context,...).{ . mContext = context; .} Copy the code

It does create ReceiverDispatcher with receiver and context, huh? Why didn’t it go to Receiver? In fact, this involves the internal design structure of the broadcast. Receiver has no cross-process communication capability, and broadcast needs AMS regulation, so there must be an object that can communicate with AMS. This object is InnerReceiver, and ReceiverDispatcher is responsible for maintaining the connection between the two, as shown below:

The onReceive method is also called from ReceiverDispatcher.

ReceiverDispatcher.java/Args.class;
public final Runnable getRunnable(a) {
    return() - > {. ;        try {
. ; // As you can see, the receiver method is called back, so that the whole process of receiving broadcasts is completed.  receiver.onReceive(mContext, intent);  }  } } Copy the code

Args is the internal class of Receiver. MContext is the object passed in when ReceiverDispatcher is created.

However, not every Activity is an Activity. In the source code, we know that we get the context by getOuterContext, and if we register the broadcast from another context, the corresponding object is different, but we usually create the broadcast in the Activity, so this context is usually an Activity object.

The context retrieval process for ContentProvider

ContextProvider we use less ContextProvider, content providers are mainly used for content sharing between applications. Although the ContentProvider is created by the system, it doesn’t belong to the Context family itself, so it gets its Context from someone else. As usual, let’s look at how to create a ContentProvider:

Yi? Isn’t this the flowchart created by Application? Yes, ContentProvider is created with application startup. Here’s a more detailed flow chart:

Let’s focus on the creation of the ContentProvider, the installContentProviders method. Similarly, a detailed ContentProvider workflow can be found in the Android ContentProvider startup and request source code flow (based on API29) article. InstallContentProviders is called in the handleBindApplication. We see where this method is called:

private void handleBindApplication(AppBindData data) {
    try {
        / / create the Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
. if(! data.restrictedBackupMode) { if(! ArrayUtils.isEmpty(data.providers)) { / / install ContentProvider  installContentProviders(app, data.providers);  }  } } Copy the code

You can see that the Application object is passed in here. Let’s continue:

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();
    for (ProviderInfo cpi : providers) {
. ContentProviderHolder cph = installProvider(context, null, cpi,  false /*noisy*/.true /*noReleaseNeeded*/.true /*stable*/); . } .} Copy the code

Here installProvider is called, and you can read on:

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
 if (holder == null || holder.provider == null) { . // where c is finally constructed by context  Context c = null;  ApplicationInfo ai = info.applicationInfo;  if (context.getPackageName().equals(ai.packageName)) {  c = context;  } . try {  / / create a ContentProvider  final java.lang.ClassLoader cl = c.getClassLoader();  LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); . localProvider = packageInfo.getAppFactory()  .instantiateProvider(cl, info.name);  provider = localProvider.getIContentProvider(); . // Set the context to ContentProvider  localProvider.attachInfo(c, info);  } . } .} Copy the code

The most important line here is localprovider.attachInfo (c, info); Set the context to ContentProvider. Let’s dig a little deeper:

ContentProvider.class(api29)
public void attachInfo(Context context.ProviderInfo info) {
    attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
. if (mContext == null) {  mContext = context; . } .} Copy the code

This does assign the context to the ContentProvider’s internal variable, mContext, so that the ContentProvider can use the context. And this context is the Application that was passed in the first place.

Look at Context from a source design perspective

So I’m done with Context here. Research Framework layer knowledge, not just what he is, what role can be. The Framework layer is a whole, which constitutes the huge system of Android. We still need to see what role Context plays and what problems it solves. In the Window mechanism, I mentioned that the existence of Window is to solve the problem of display logic and touch feedback of the view on the screen. In the Hanlder mechanism, I wrote that the entire Android program is driven by the Handler mechanism, and the Context?

The Android system is a whole ecosystem, and it builds an environment on which applications can run. And any program that wants to run on this environment has to get permission from the system, which is software installation. Unlike computers, Android does not allow any program to directly access the system’s resources. We can write a Java program on the Window and open a file stream to read and modify the file. Android is not so simple, any of his procedures must run through the system’s control. That is, even if the application is allowed (installed on the phone), the application itself has to run and the system has to control the application, and the application cannot automatically execute in the Android environment. We can see from the source code that the main method of the program simply starts the Looper loop of the thread, and everything else must wait for AMS to control.

Can the application just execute itself? Yes, but it won’t work. To obtain system resources, such as starting four components, reading layout files, reading and writing database, calling system cabinet camera, etc., Context must be obtained through AMS. This makes the difference between a regular Java program and an Android program.

Context has two important responsibilities: identity permissions and the interface through which an application accesses the system. A Java class without a context is a normal Java class, but when it obtains the context it is called a component because it has access to the system. It is no longer a normal identity, but an Android “citizen”. The “citizen” is not lawless, and the system can also use context to encapsulate and limit the permissions of the program. In order for a notification to pop up, you have to go through this API, and the user turns off your notification permission, so you can’t pop up a notification the second way. And the program doesn’t need to know how the underlying implementation works, just call the API. The big four components are called the Big Four because they are born with context, especially activities and services, including applications. And everything we write, we have to get the context either indirectly or directly from it.

In short, context is a mechanism for differentiating between applications inside and outside Android, limiting access to system resources.

The last

This series of articles starts with the introduction of what context is, then analyzes the different subclasses of context, and finally explains the creation process of context in depth with source code. Finally, I’m going to talk about my design understanding of context.

That’s all I want to say about context. Although they are rarely used on a daily basis, they are very helpful in understanding the overall framework of the Android system. And when we have a deeper understanding of the system, the written programs are much more robust.

Hope this article is helpful.

Full text here, the original is not easy, feel help can like collection comments forward. I have no talent, any ideas welcome to comment area exchange correction. If need to reprint please private communication.

And welcome to my blog: Portal