BroadcastReceiver的跨进程注册、接收流程源码解析

根据《Activity跨进程启动流程源码探究》我们可以清楚以下几点:
1)Context的通用实现是在ContextImpl这个类中
2)Activity的启动过程需要借助ActivityManagerService(AMS)这个服务端来完成,其本质是通过Binder通信。
3)核心的Binder通信使用了2次,第一次Context作为客户端向AMS发起start请求,第二次AMS作为客户端向IApplicationThread发起最终的启动请求,我们暂且称为“双Binder切换机制”。
4)第二次Binder通信后,通过H这个Handler进行线程切换,并且切回了主线程。Application、Activity、ContextImpl等实例创建都在主线程完成的。
鉴于此,我们分析BroadcastReceiver的注册过程从ContextImpl的registerReceiver方法开始。

1.注册第1次Binder机制

在ContextImpl的结构图中,我们可以看到Context通过registerReceiver和registerReceiverAsUser两个方法都可以注册广播,但最终都会调用registerReceiverInternal去注册。看一下这个方法:

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;  // 1-0 初始化IIntentReceiver这个Binder接口
        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 {  // 1-1作为客户端向AMS发起注册请求,第2次Binder机制
            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();
        }
    }

在代码1-0开始的第一个条件内,完成对IIntentReceiver这个Binder接口的实例化。在实例化过程中,传入了scheduler这个ActivityThread.H,目的肯定是为了切回主线程,跟《Service通过onBind启动流程源码探究》一个路子。需要注意的是这一次LoadedApk.ReceiverDispatcher.InnerReceiver子类作为服务端完成实例化的具体工作。同样,在ReceiverDispatcher的构造方法中,也有如下代码引用:

mActivityThread = activityThread;  // H 

看来是同一个人写的bindService和registerReceiver机制。
在代码1-1开始的try代码块内完成对AMS的注册请求,也即是第2次Binder机制。

2.注册第二次Binder机制

ContextImpl作为客户端调用ActivityManager.getService().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags)方法,并将第1次Binder中生成的IIntentReceiver实例传给AMS这个服务端,AMS的registerReceiver方法最终会调用如下核心代码:

mRegisteredReceivers.put(receiver.asBinder(), rl);

其中mRegisteredReceivers是一个HashMap,其定义如下:

    /** Keeps track of all IIntentReceivers that have been registered for broadcasts.    
      * Hash keys are the receiver IBinder, hash value is a ReceiverList.    
      */ 
     final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

可见,所谓的注册就是把BroadcastReceiver保存在HashMap里。
当然过滤器的注册过程,也是将BroadcastFilter保存在容器里,核心代码如下:

            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            rl.add(bf);

不同的是,rl是一个ArrayList。其定义如下:

/**
 * A receiver object that has registered for one or more broadcasts.The ArrayList holds 
 * BroadcastFilter objects.
 */
 final class ReceiverList extends ArrayList<BroadcastFilter> implements IBinder.DeathRecipient {

这样广播的注册过程就完成了。但是,onRecieve方法还未调用。

3.接收第一次Binder机制

注册完BroadcastReceiver这个接收器,它就可明目张胆地的接收广播了,而广播的接收还需要从发送谈起。在ContextImpl中提供的方法有:sendBroadcast、sendBroadcastAsUser、sendBroadcastMultiplePermissions、sendOrderedBroadcast、sendStickyBroadcast、sendStickyBroadcastAsUser,尽管他们有很多重载类型,内部都不约而同地调用了AMS的broadcastIntent方法,核心代码就1行,如下:

ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(),
                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());

《Activity启动流程源码探究》中我们已经提到过,ActivityManagerService其实是IActivityManager的具体实现,这是一次很明显的Binder通信,ContextImpl是客户端向服务端AMS发起发送广播的请求。这个过程是通过broadcastIntent方法来完成的。它内部调用了AMS#broadcastIntentLocked方法,在该方法内部首先给当前广播加了一个flag,核心代码如下:

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

也就是默认的情况下,是不会把该广播发送给已经停止的APP的。然后,该方法根据通过intent携带的信息找到所有对该广播注册的BroadcastReceiver,并存入了registeredReceivers这个列表里。核心代码如下:

        // Figure out who all will receive this broadcast.
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    ......
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            }
        }

注意这里的users[i],因为注册广播的用户不止一个,所以要拿到各个用户的注册信息。接着,将我们得到registeredReceivers封装在BroadcastRecord中,核心代码如下:

        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);

        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);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending
                    && (queue.replaceParallelBroadcastLocked(r) != null);
            // Note: We assume resultTo is null for non-ordered broadcasts.
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

注意,我们的普通广播时没有Intent.FLAG_RECEIVER_REPLACE_PENDING)标记的,所以replacePending是false,replaced自然也是false,代码调用queue.scheduleBroadcastsLocked()方法。点击进入该方法,它通过Handler做了消息切换,代码如下:

    public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

搜索BROADCAST_INTENT_MSG这条消息,发现processNextBroadcast处理了这条消息,并且通过两个循环轮询广播注册信息,核心代码如下:

            // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                ......
                final int N = r.receivers.size();
                
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                            "Delivering non-ordered on [" + mQueueName + "] to registered "
                            + target + ": " + r);
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }

for循环内调用了deliverToRegisteredReceiverLocked方法,注意它的参数target,它是我们的目标。每个广播接收器都能被准确调用的关键是这个target里携带了重要信息,进入deliverToRegisteredReceiverLocked方法,这个代码最终会调用performReceiveLocked这个终极boss,完成广播队列的处理逻辑,调用逻辑如下:

        try {
            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                    "Delivering to " + filter + " : " + r);
            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
                // Skip delivery if full backup in progress
                // If it's an ordered broadcast, we need to continue to the next receiver.
                if (ordered) {
                    skipReceiverLocked(r);
                }
            } else {
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
            }
            if (ordered) {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
            ......
        }

注意,由于我们分析的是首次发送广播,所以默认进程是没有启动的,那么filter.receiverList.app.inFullBackup的值就是false,不存在后台进程的情况。这里的filter当然就是我们刚刚传入的target,也即是BroadcastFilter的实例,receiverList是该实例的一个字段,在ReceiverList里面还封装了我们的ProcessReccord信息,也就是filter.receiverList.app这个变量。它作为参数传给了performReceiveLocked。该方法比较关键,它根据目标进程是否启动,实现了两个逻辑,核心代码如下:

 void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Send the intent to the receiver asynchronously using one-way binder calls.
        if (app != null) {
            if (app.thread != null) {
                // If we have an app thread, do the call through that so it is
                // correctly ordered with other one-way calls.
                try {
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                    .......
                }
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser);
        }

如果目标Process存在已经在运行,通过app.thread.scheduleRegisteredReceiver执行接收工作。否则,直接通过receiver.performReceive执行接收任务,注意:这里的receiver就是我们在第1节,注册第一次Binder机制中传给AMS的IIntentReceiver实例,也就是LoadedApk.ReceiverDispatcher.InnerReceiver的实例。根据前面的信息,我们知道app这个目标ProcessRecRecord不为空,我们自然要从app.thread.scheduleRegisteredReceiver入手分析,也就是接收第二次Binder机制,这里的app.thread就是ActivityThread.ApplicationThread。

4.接收第二次Binder机制

app.thread.scheduleRegisteredReceiver方法只是做了一次简单的桥接,就将任务交给receiver.performReceive了,该方法的全部代码如下:

        public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException {
            updateProcessState(processState, false);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

这算是最直率的一次Binder了。接着来看receiver.performReceive的执行逻辑,注意:这里的receiver就是我们在第1节,注册第一次Binder机制中传给AMS的IIntentReceiver实例,也就是LoadedApk.ReceiverDispatcher.InnerReceiver的实例。这算是接收第三次Binder机制。值得注意的是,这一次Binder机制跟第1节提到的注册第一次Binder机制属于同一次Binder,他们就是同一个Binder线程。

5.接收第三次Binder机制

在LoadedApk.ReceiverDispatcher.InnerReceiver#performReceive方法中,只有1行代码值得注意,那就是如下这个条件:

   if (intent == null || !mActivityThread.post(args.getRunnable())) {

这里的mActivityThread就是我们在第1节《注册第一次Binder机制》中提到的ActivityThread的H这个Handler,它直接将Runnable中的任务抛回主线程。点击进入getRunnable()这个方法,会发现如下核心代码:

public final Runnable getRunnable() {
   return () -> {
      try {
            ...
            receiver.onReceive(mContext, intent);
}

我们注册的BroadcastReceiver.onReceive方法被调用了。

5.总结

  1. BroadcastReceiver的注册过程不需要创建ContextImpl、Application、BroadcastReceiver等,这与Activity和Service不一样。原因是BroadcastReceiver并不是Context的子类,它的实例不依赖环境。
  2. BroadcastReceiver的onReceive方法是在主线程调用的,会阻塞主线程,这跟ServiceConnection的onServiceConnected的调用一致,不可在其内部进行耗时操作。
  3. 广播的注册过程本质上就是把BroadcastReceiver保存在HashMap里,把IntentFilter保存在List中的过程。
  4. 我们只是分析了动态注册广播,静态注册是PMS(PackageManagerService的简写)解析AndroidManifest文件自动完成的。
  5. BroadcastReceiver的注册、接收过程涉及5次核心的Binder通信。其中,注册2次,接收3次,其中一个Binder线程供注册注册初始化IIntentReceiver实例和接收调用onReceive接口合用。
  6. 广播发送者会根据intent找到注册广播的接收器,每个接收器都携带了自己的进程记录信息,这样不管有多少个广播接收器,都能被准确定位。

诗云:

草木之春不久归,百般红紫斗芳菲。
杨花榆荚无才思,惟解漫天作雪飞!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值