Android源码分析 (九) BroadcastReceiver 注册过程

广播的注册过程

首先,想要使用广播就得通过 registerReceiver 方法注册一个广播,最终也是在 Activity 的父类 ContextWrapper 中实现,代码如下:

//ContextWrapper.java
		/**
     * 应用调用注册广播的函数
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        //mBase 是 Context的引用
        return mBase.registerReceiver(receiver, filter);
    }

直接看 Context 的实现类 ContextImpl 中的实现,代码如下:

    /**
     * Context 调用
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        //调用内部重载方法
        return registerReceiver(receiver, filter, null, null);
    }



    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                                   String broadcastPermission, Handler scheduler) {
        //调用内部重载方法
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }


    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                                            IntentFilter filter, String broadcastPermission,
                                            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
             1
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    //获取 ActivityThread H
                    scheduler = mMainThread.getHandler();
                }
                //获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
                rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                       2
                        mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                3
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            //调用 AMS 的 registerReceiver
            4
            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 判断 mPackageInfo 与 context 是否为空

2 通过 mPackageInfo 方法调用 getReceiverDispatch 方法获取 rd 对象

3 处的代码来创建 rd 对象

2 步代码主要就是来获取 IIntentReceiver 对象,它是一个 Binder 接口,用于广播的跨进程通信,它在在 LoadedApk.ReceiverDispatcher.InnerReceiver 中实现,代码如下所示:

//LoadedApk.java
    static final class ReceiverDispatcher {

        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }
          
          ....
            
        }

回到 registerReceiverInternal 方法,在注释 4 处调用 AMS 代理类 IActivityManager 的 registerReceiver 方法,通过进程间通信最后在 AMS 类的 registerReceiver 方法,并将 IIntentReceiver 类型的 rd 引用也一并传递进 AMS 中,这里之所以不直接传入客户端的 BroadcastReceiver 而是传入 IIntetnReceiver ,因为注册广播是一个跨进程的过程,需要具有跨进程通信功能的 IIntentReceiver ,你也可以把它理解为一个中间者,registerReceiver 方法内部比较多,这里分为两部分

我们先看

registerReceiver 方法的第一部分

//AMS.java
    /**
     * 这里是通过 Binder 通知调用
     * @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 boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                1
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            2
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                
                String action = actions.next();
                for (int id : userIds) {
                    //根据actions 列表和 userIds 得到所有的粘性广播的 intent,并在注释 3 处传入到 stickyIntents 中
                    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>();
                            }
                            3 
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // Look for any matching sticky broadcasts...
            /**
             * 遍历寻找匹配的粘性广播
             */
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // Don't provided intents that aren't available to instant apps.
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                    continue;
                }
                //开始匹配
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    4
                    allSticky.add(intent);
                }
            }
        }

        ....//后面代码 第二部分 讲解

            return sticky;
        }
    }

总结如上

1 通过 getRecordForAppLocked 方法得到 ProcessRecord 类型的 callerApp 对象,它用于描述请求 AMS 注册广播接收者的 Activity 所在的应用程序进程

2 处根据传入的 IntentFilter 类型的 filter 得到一个 actions 列表,根据 actions 列表和 userIds 得到所有的粘性广播的 intent 

3 处传入到 stickyIntents 中。接下来从 stickyIntent 中匹配传入的参数 filter 的粘性广播的 intent

4 处将这些 intent 存入到 allSticky 列表中,从这里可以看出粘性广播是存储在 AMS 中的。

registerReceiver 方法的 第二部分

接下来看 AMS 的 registerReceiver 方法剩余内容,代码如下所示:

//AMS.java

    /**
     * 这里是通过 Binder 通知调用
     * @return Intent
     */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
     ...
       
       
          synchronized (this) {
					...
            1
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {//如果为空调用注释 2
               2
                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) {
                ...
            }

           3
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            4
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
             5
            mReceiverResolver.addFilter(bf);

    
      ...
    }

总结 如上代码

1. 获取 ReceiverList 

2. 创建一个 ReceiverList 对象,它继承自 ArrayList,用于存放广播接收者

3 构建 BroadcastFilter 对象,并且将 广播接收者列表 ReceiverList 添加到进去 * 用于描述注册的广播接收者

4. 通过 add 函数将自身添加到广播接收者列表中

5. 将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中

在注释 1 处获取到了 ReceiverList 列表,如果为空则在注释 2 处创建一个新的接收列表 ReceiverList 对象,它继承自 ArrayList,用来存储广播接收者。在注释 3 处创建 BroadcastFilter 并传入此前创建的 ReceiverList ,BroadcastFilter 用来描述注册的广播接收者,并在注释 4 处通过 add 方法将自身添加到 ReceiverList 中。在注释 5 处将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中,这样当 AMS 接收到广播时就可以从 mReceiverResolver 中找到对应的广播接收者了。从而达到注册广播的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值