Advanced knowledge of Android: working process of BroadcastReceiver

Keywords: Android

Advanced knowledge of Android (29): working process of BroadcastReceiver

BroadcastReceiver is a message type component, which is used to deliver messages between different components and even different applications. The working process of broadcast receiver mainly includes two aspects, one is the registration process of broadcast, the other is the sending and receiving process of broadcast.
                             

1, Registration process of BroadcastReceiver

The registration of    broadcast is divided into static registration (registered in the Android manifest file) and dynamic registration (code registration). When the application is installed, the system automatically registers the static registered broadcast. Specifically, PMS (PackageManagerService) completes the whole registration process. In addition to broadcasting, the other three components are also resolved and registered by PMS during application installation.

   here we only analyze the dynamic registration process of broadcast, and the specific process is shown in the figure below.

   where, the black dotted arrow indicates that the overloaded method is called, and the final call arrow points to the method; the purple dotted arrow indicates the IPC procedure of the remote call. There are several points to be aware of in the registration process of BrocadcastReceiver.

  1. The registration process starts with ContextWrapper

                          .

  1. registerReceiverInternal method of ContextImpl

The registerReceiverInternal method source code of   ContextImpl is as follows.

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) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = mPackageInfo.getReceiverDispatcher(
                receiver, context, scheduler,
                mMainThread.getInstrumentation(), true);
        } else {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = new LoadedApk.ReceiverDispatcher(
                    receiver, context, scheduler, null, true).getIIntentReceiver();
        }
    }
    try {
        final Intent intent = ActivityManager.getService().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

                           .
                             ReceiverDispatcher.InnerReceiver Second, the receiverdispatcher stores both the BroadcastReceiver and innerreceiver in order to facilitate the subsequent broad Call to the onReceive method of the castreceiver.

  1. The registerReceiver method of the registration process only saves and does not call back

The most different point in the registration process of    broadcast is that after calling AMS's registerReceiver remotely, the method only saves the InnerReceiver object and the IntentFilter object, without the need for remote method callback like Activity and Service process. The core code is as follows.

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
        int flags) {
    // ...
    // Save the IntentReceiver object
    mRegisteredReceivers.put(receiver.asBinder(), rl);            
    // ...
    // Save the IntentFilter object
    BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
        permission, callingUid, userId, instantApp, visibleToInstantApps);
    if (rl.containsFilter(filter)) {
        Slog.w(TAG, "Receiver with filter " + filter
            + " already registered for pid " + rl.pid
            + ", callerPackage is " + callerPackage);
    } else {
        rl.add(bf);
        if (!bf.debugCheck()) {
            Slog.w(TAG, "==> For Dynamic broadcast");
        }
        mReceiverResolver.addFilter(bf);
    }
    // ...
}

                 .

2, The sending and receiving process of BroadcastReceiver

                        . There are several types of Broadcasting: General broadcasting, ordered broadcasting and sticky broadcasting. Even if the characteristics are different, the processes are similar. So the author takes general broadcasting as an example to analyze.

The following points should be paid attention to in the process of transmitting and receiving broadcast.

  1. Broadcast will not be sent to stopped apps

The broadcastIntentLocked method is complex, but there is a line in the code.

// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

   starting from Android 3.1 (Android 10.0 is used here) broadcasting has a feature that by default, broadcasting will not be sent to stopped applications. In Android 3.1, two flag bits are added for Intent, namely FLAG_INCLUDE_STOPPED_PACKAGES and flags_ EXCLUDE_ STOPPED_ Packages, which is used to control whether the broadcast will play a role in the application in the stopped state. The specific meaning is as follows.

Marker bit meaning
FLAG_INCLUDE_STOPPED_PACKAGES Indicates that the application that has been stopped is included. At this time, the broadcast will be sent to the application that has been stopped
FLAG_EXCLUDE_STOPPED_PACKAGES Indicates that there is no application that has been stopped. At this time, the broadcast will not be sent to the application that has been stopped

                       . If you need to start the stopped application, you only need to add flag for the broadcast Intent_ EXCLUDE_ STOPPED_ The packages flag bit is enough. When the two tags coexist, flag is used_ EXCLUDE_ STOPPED_ Packages shall prevail.

  1. broadcastIntentLocked works

                                . Part of the source code is shown below.

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
    // If we are not serializing this broadcast, then send the
    // registered receivers separately so they don't wait for the
    // components to be launched.
    if (isCallerSystem) {
        checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                isProtectedBroadcast, registeredReceivers);
    }
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
            requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
            resultCode, resultData, resultExtras, ordered, sticky, false, userId,
            allowBackgroundActivityStarts, timeoutExempt);
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
    final boolean replaced = replacePending
            && (queue.replaceParallelBroadcastLocked(r) != null); // Find filter
    // Note: We assume resultTo is null for non-ordered broadcasts.
    if (!replaced) {
        // Save general broadcast and send
        queue.enqueueParallelBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
    registeredReceivers = null;
    NR = 0;
}

                   .

Reference: Android development art exploration

Posted by CallItKarma on Wed, 10 Jun 2020 21:24:05 -0700