introduce

BroadcastReceiver is one of the four components of Android. It functions as an important communication mode between applications and processes. BroadcastReceiver can transmit a certain message to the subscribed BroadcastReceiver through broadcast. Below we will analyze the broadcast registration to receive the message Android source code exactly what to do?

The source code parsing

registerReceiver

Sequence diagram

The code on

Let’s walk through the code with the sequence diagram above

  1. The MainActivity registerReceiver first broadcasts the receiver

    public class MainActivity extends Activity {
          @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
          	// Register a broadcast receiver
    				registerReceiver(newReceiverB(),filterB); }... Code omission... }Copy the code

Click registerReceiver to see that it is not a method in the Activity, but ContextWrapper, the parent class of the Activity

  1. ContextWrapper registerReceiver

    public class Activity extends ContextThemeWrapper
            implements LayoutInflater.Factory2.Window.Callback.KeyEvent.Callback.OnCreateContextMenuListener.ComponentCallbacks2.Window.OnWindowDismissedCallback.WindowControllerCallback.AutofillManager.AutofillClient {... Code omission... }Copy the code
    public class ContextThemeWrapper extends ContextWrapper {... Code omission... }Copy the code
    public class ContextWrapper extends Context {
          @Override
        public Intent registerReceiver( BroadcastReceiver receiver, IntentFilter filter) {
            returnmBase.registerReceiver(receiver, filter); }}Copy the code

    ContextImp inherits the Context (mBase); ContextImp inherits the Context (mBase); ContextImp inherits the Context (mBase);

  2. ContextImp registerReceiver

    class ContextImpl extends Context {   
    @Override
        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                String broadcastPermission, Handler scheduler, int flags) {
            returnregisterReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), flags); }}Copy the code
  3. Continue to see registerReceiverInternal

        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) {
                        // Get ActivityThread H
                        scheduler = mMainThread.getHandler();
                    }
                    // Get the IIntentReceiver object, which interacts with AMS and passes messages through a Handler
                    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 {
                // Call AMS registerReceiver
                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) {
                throwe.rethrowFromSystemServer(); }}Copy the code

    The function that registers the broadcast receiver ends up in ContextImpl’s registerReceiverInternal function, where the member variable mPackageInfo is an instance of LoadApk that handles the reception of the broadcast.

  4. ActivityThread H

    The Handler obtained by mmainThread.gethandler () is used to distribute broadcasts from AMS.

     private class H extends Handler {...public void handleMessage(Message msg) {
       case RECEIVER:                
            handleReceiver((ReceiverData)msg.obj);
                        maybeSnapshot();
                       
         break; }... }Copy the code
  5. Continue to see handleReceiver

        private void handleReceiver(ReceiverData data) {...// Apply the Application local variable
            Application app;
            // Broadcast local variables
            BroadcastReceiver receiver;
            / / application Context
            ContextImpl context;
            try {
                / / make Applicaiton
                app = packageInfo.makeApplication(false, mInstrumentation);
                // Get the context
                context = (ContextImpl) app.getBaseContext();
                if(data.info.splitName ! =null) {
                    context = (ContextImpl) context.createContextForSplit(data.info.splitName);
                }
                java.lang.ClassLoader cl = context.getClassLoader();
                data.intent.setExtrasClassLoader(cl);
                data.intent.prepareToEnterProcess();
                data.setExtrasClassLoader(cl);
                // Instantiate broadcast via reflection
                receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
            } catch (Exception e) {
            try {
                if (localLOGV) Slog.v(
                sCurrentBroadcastIntent.set(data.intent);
                receiver.setPendingResult(data);
                // call onReceive
            receiver.onReceive(context.getReceiverRestrictedContext(),
                        data.intent);
            } catch (Exception e) {
               ...
            } finally {
                sCurrentBroadcastIntent.set(null); }... }Copy the code
  6. MPackageInfo getReceiverDispatcher function

    //LoadeApk
        public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
                Context context, Handler handler,
                Instrumentation instrumentation, boolean registered) {
            synchronized (mReceivers) {
                LoadedApk.ReceiverDispatcher rd = null;
                ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
                if (registered) {
                    map = mReceivers.get(context);
                    if(map ! =null) { rd = map.get(r); }}if (rd == null) {
                    rd = new ReceiverDispatcher(r, context, handler,
                            instrumentation, registered);
                    if (registered) {
                        if (map == null) {
                            map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                            // The Context key and the map values are stored in mReveiversmReceivers.put(context, map); } map.put(r, rd); }}else {
                    rd.validate(context, handler);
                }
                rd.mForgotten = false;
                returnrd.getIIntentReceiver(); }}Copy the code

    In the getReceivcerDispatcher function of the LoadedAPK class, first check to see if r is already instantiated. If not, create one and store it in a HM collection with r as the key. The map is stored in the mReceivers, so that given an Activity and BroadcastReceiver, we can check whether the broadcast receivers already exist in the LoadedAPK.

    Now, in the back to ContextImpl registerReceiverInternal function, we obtain the IIntentReceiver type Binder object, began to register in the AMS, specific code below.

  7. AMS registerReceiver

        /** * this is called with Binder notification *@return Intent
         */
        public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
                int flags) {
            enforceNotIsolatedCaller("registerReceiver");
            ArrayList<Intent> stickyIntents = null;
            ProcessRecord callerApp = null;
            final booleanvisibleToInstantApps = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) ! =0;
            int callingUid;
            int callingPid;
            boolean instantApp;
            synchronized(this) {
                if(caller ! =null) {
                    //1. Get ProcessRecord
                    callerApp = getRecordForAppLocked(caller);
                    if (callerApp == null) {... Code omission... }... Code omission...//2. Search for matching sticky receivers according to Action
                Iterator<String> actions = filter.actionsIterator();
                if (actions == null) {
                    ArrayList<String> noAction = new ArrayList<String>(1);
                    noAction.add(null); actions = noAction.iterator(); }... Code omission.../ / 3. Get ReceiverList
                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
                if (rl == null) {
                    rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                            userId, receiver);
                    if(rl.app ! =null) {
                        rl.app.receivers.add(rl);
                    } else {
                        try {
                            receiver.asBinder().linkToDeath(rl, 0);
                        } catch (RemoteException e) {
                            return sticky;
                        }
                        rl.linkedToDeath = true;
                    }
                    mRegisteredReceivers.put(receiver.asBinder(), rl);
                } else if(rl.uid ! = callingUid) { ... Code omission... }//4. Build the BroadcastFilter object and add it to the ReceiverList
                BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                        permission, callingUid, userId, instantApp, visibleToInstantApps);
                rl.add(bf);
                if(! bf.debugCheck()) { Slog.w(TAG,"==> For Dynamic broadcast"); } mReceiverResolver.addFilter(bf); . Code omission...returnsticky; }}Copy the code
    • According to note 1 above, the pid and UID corresponding to the process are obtained.

    • Comment 2: Get all IntentFilter actions;

    • Note 3 saves the broadcast receiver’s receiver to a ReceiverList hosted by the rl.app process that MainActivity is running on.

    • Add BroadcastFilter (BroadcastFilter, BroadcastFilter, BroadcastFilter, BroadcastFilter, BroadcastFilter, BroadcastFilter, BroadcastFilter) It is then stored in the AMS member variable mReceiverResolver. This saves the broadcast receiver and the receiver filter of the broadcast type to be received in AMS so that the corresponding broadcast can be received and processed later.

Next, take a look at sendBroadcast().

onReceive

Sequence diagram

  1. After the Activity sends a broadcast via sendBroadcast, Binder sends it to AMS. AMS finds the corresponding broadcast receiver based on the Action type of the broadcast, and then puts the broadcast into its message queue to complete the first part of asynchronous broadcast distribution.

        final int broadcastIntentLocked(ProcessRecord callerApp,
                String callerPackage, Intent intent, String resolvedType,
                IIntentReceiver resultTo, int resultCode, String resultData,
                Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
                boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
            intent = newIntent(intent); . Code omission....if (intent.getComponent() == null) {... Code omission.... }else {
                    // The BroadcastFilter corresponding to the Intent is displayed
                    registeredReceivers = mReceiverResolver.queryIntent(intent,
                            resolvedType, false /*defaultOnly*/, userId); }}... Code omission....if(! replaced) { queue.enqueueParallelBroadcastLocked(r);// Handle broadcast distribution
                    queue.scheduleBroadcastsLocked();
                }
                registeredReceivers = null;
                NR = 0; }... Code omission....return ActivityManager.BROADCAST_SUCCESS;
        }
    Copy the code
  2. AMS processes this broadcast in a message loop and distributes it to the registered ReceiverDispatch through Binder mechanism, which places the broadcast in the message queue of the MainActivity process. Complete part 2 asynchronous message distribution.

        public void scheduleBroadcastsLocked(a) {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                    + mQueueName + "]: current="
                    + mBroadcastsScheduled);
    
            if (mBroadcastsScheduled) {
                return;
            }
            // Distributed by Handler
            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
            mBroadcastsScheduled = true;
        }
    Copy the code
        private final class BroadcastHandler extends Handler {...@Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case BROADCAST_INTENT_MSG: {
                        if (DEBUG_BROADCAST) Slog.v(
                                TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                        // Process the next broadcast
                        processNextBroadcast(true);
                    } break; . }}}Copy the code
  3. ReceiverDispatch’s internal class Args processes the broadcast in the thread message loop where MainActivity resides and ultimately distributes the broadcast to the onReceiver processing of the registered Receiver instance.

    public final class LoadedApk {...static final class ReceiverDispatcher {
           
               final class Args extends BroadcastReceiver.PendingResult {...public final Runnable getRunnable(a) {
                    return() - > {...try {
                            ClassLoader cl = mReceiver.getClass().getClassLoader();
                            intent.setExtrasClassLoader(cl);
                            intent.prepareToEnterProcess();
                            setExtrasClassLoader(cl);
                            receiver.setPendingResult(this);
                            // Callback to the onReceiver that receives the broadcast
                            receiver.onReceive(mContext, intent);
                        } catch(Exception e) { ... }; }}}... }Copy the code

conclusion

BroadcastReceiver is a subscription-publish process. The BroadcastReceiver is stored in a map, and the key encapsulates the broadcast information classes, such as actions, and so on. When publishing a broadcast, AMS queries BroadcastReceiver of IntentFilter registered with the broadcast in the map, and then distributes the broadcast to each subscribed object through ReceiverDispatch, thus completing the whole communication process.

Thank you for reading thank you!