Broadcast注册流程

1,867 阅读4分钟

文中的源代码版本为api23

广播注册分为两种:动态注册、静态注册。 静态注册只需要在Manifest文件中声明即可。 而动态注册则需要通过Context.registerReceiver方法进行注册。下面我们分析一下动态注册的过程。

动态广播注册

动态注册通过调用Context.registerReceiver方法实现。该方法会经历以下调用链 Context.registerReceiver(BroadcastReceiver,IntentFilter)-> ContextImpl.registerReceiver(BroadcastReceiver,IntentFilter)-> ContextImpl.registerReceiver(BroadcastReceiver,IntentFilter,String,Handler)-> ContextImpl.registerReceiverInternal

最终进入ContextImpl.registerReceiverInternal方法中

1. ContextImpl.registerReceiverInternal方法

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter,
            String broadcastPermission,//广播发送者需要具备该权限才能发广播给这个接收者
            Handler scheduler,//scheduler可以控制广播接收者运行所在的线程
            Context context) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            //将receiver包装成一个aidl接口
            rd = mPackageInfo.getReceiverDispatcher(
                receiver, context, scheduler,
                mMainThread.getInstrumentation(), true);
        } else {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            //将receiver包装成一个aidl接口
            rd = new LoadedApk.ReceiverDispatcher(
                    receiver, context, scheduler, null, true).getIIntentReceiver();
        }
    }
    try {
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission, userId);
    } catch (RemoteException e) {
        return null;
    }
}

该方法结构较简单,做了两件事情

  1. receiver包装成一个aild接口(IIntentReceiver)。后续AMS可以直接与该接口进行通信,分发广播事件。
  2. 跨进程调用ActivityManagerService.registerReceiver方法

2. ActivityManagerService.registerReceiver方法

该方法主要做了两件事情

  1. AMS中保存广播接收者(实际上就是IIntentReceiver这个aidl接口)
  2. 发送粘性广播 其中粘性广播的逻辑占据了非常大的篇幅

1. 保存广播接收者

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                                   IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
            //...
            //mRegisteredReceivers保存着所有的广播接收者的binder对象
            //key为IIntentReceiver的binder对象
            //value为ReceiverList,该数据结构的作用是建立起广播接收者与广播之间的联系
            //由于广播接收者和广播是一对多关系,因此ReceiverList派生了ArrayList
            //第一次进来rl肯定是null
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                //创建并保存ReceiverList
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    //ProcessRecord也会记录ReceiverList
                    rl.app.receivers.add(rl);
                } else {
                    //...
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                                + " was previously registered for uid " + rl.uid);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                                + " was previously registered for pid " + rl.pid);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                                + " was previously registered for user " + rl.userId);
            }
            // 创建一个BroadcastFilter
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            // 保存BroadcastFilter至ReceiverList
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            //添加至BroadcastFilter保存至BroadcastFilter,在发送广播的时候会用到
            mReceiverResolver.addFilter(bf);
            //...
}

总结一下流程

  1. 将广播接收者封装成ReceiverList对象保存至mRegisteredReceivers
  2. 封装IntentFilterBroadcastFilter保存至ReceiverList(ReceiverList本身派生自ArrayList) 可以看到在AMS中广播接收者和IntentFilter是一对多的关系,因此我们可以使用一个广播接收者调用多次registerReceiver,来实现一个广播接收者监听多个广播的功能。

2. 发送粘性广播

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                                   IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {

    //...

    //寻找粘性广播,保存至allSticky
    synchronized (this) {
        //...
        Iterator<String> actions = filter.actionsIterator();
        //...

        // 所有的粘性广播都保存在一个名为mStickyBroadcasts的变量中
        // 类型为SparseArray<ArrayMap<String, ArrayList<Intent>>>
        // SparseArray的key是userId,value是一个Map类型,该Map类型的
        // key为action,value则是action对应的所有的粘性广播
        
        // 以action为维度寻找当前用户下所有符合条件的粘性广播
        // 并保存至stickyIntents中
        int[] userIds = {UserHandle.USER_ALL, UserHandle.getUserId(callingUid)};
        while (actions.hasNext()) {
            String action = actions.next();
            for (int id : userIds) {
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                if (stickies != null) {
                    ArrayList<Intent> intents = stickies.get(action);
                    if (intents != null) {
                        if (stickyIntents == null) {
                            stickyIntents = new ArrayList<Intent>();
                        }
                        stickyIntents.addAll(intents);
                    }
                }
            }
        }
    }

    // 遍历stickyIntents,以更加严苛的条件去匹配
    // IntentFilter.match方法内部会就mimetype、scheme、data等
    // 变量进行匹配
    // 将所有符合条件的粘性广播保存至allSticky
    ArrayList<Intent> allSticky = null;
    if (stickyIntents != null) {
        final ContentResolver resolver = mContext.getContentResolver();
        for (int i = 0, N = stickyIntents.size(); i < N; i++) {
            Intent intent = stickyIntents.get(i);
            if (filter.match(resolver, intent, true, TAG) >= 0) {
                if (allSticky == null) {
                    allSticky = new ArrayList<Intent>();
                }
                allSticky.add(intent);
            }
        }
    }

    //...

    synchronized (this) {
        
        //...保存receiver

        if (allSticky != null) {
            //...

            //遍历所有粘性广播,使用BroadcastQueue进行广播
            final int stickyCount = allSticky.size();
            for (int i = 0; i < stickyCount; i++) {
                Intent intent = allSticky.get(i);
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                //将粘性广播封装成BroadcastRecord
                BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                        null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                        null, 0, null, null, false, true, true, -1);
                //插入到广播队列中
                queue.enqueueParallelBroadcastLocked(r);
                //开始发送粘性广播
                queue.scheduleBroadcastsLocked();
            }
        }

        return sticky;
    }
}

发送粘性广播的流程总结下来只有两步

  1. 寻找符合IntentFilter条件的粘性广播
  2. 将粘性广播封装成BroadcastRecord插入到广播队列中,然后通过BroadcastQueue发送粘性广播

在分析粘性广播寻找过程中我一度以为mStickyBroadcastskeyuid(每个app一个),写了一些测试代码才发现其实是user id,这两个值其实是有区别的。如果大家感兴趣可以自行百度UserHandle。 由于广播的发送都是使用BroadcastQueue.scheduleBroadcastsLocked方法,因此关于粘性广播的具体发送过程,我们在分析广播发送流程的时候再讲。

3. 总结

动态广播注册过程概括来讲其实只做了两件事情

  1. 保存广播(也即是Binder对象)
  2. 发送粘性广播