[framework]简析android中动态广播从注册(registerReceiver)到接收(onReceive)的过程

之前和同事讨论动态广播和静态广播差异性问题时,发现自己对广播的内部机制毫无了解(知识空白区 T/\T)
所以,要翻开代码看看里面到底啥子来头。

动态广播的注册

首先,先整理一波动态注册的鸟瞰图
在这里插入图片描述
从图中可以看到
1)代码中调用的egisterReceiver(BroadcastReceiver, IntentFilter),在ContextImpl.java中实现(关于Context和Activity的关联,可以翻看我的前一篇博文),实际真正是ActivityManagerService去注册广播。
2)再通过跨进程通讯(这里采用的是binder),来获取用注册广播的服务,该服务的实现类在ActivityServiceManager.java。

接下来就开始真正上源码噜~
在ContextImpl.java

@Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler, int flags) {
            //从这里调进去
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), flags);
    }

调用了ContextImpl .egisterReceiverInternal()

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
              if (receiver != null) {
	            if (mPackageInfo != null && context != null) {
	                if (scheduler == null) {
	                    scheduler = mMainThread.getHandler();
	                    //mMainThread是ActivityThread的实例,获取Handler
	                }
	                //mPackageInfo是该context对象与当前进程关联的LoadedApk的实例
	                rd = mPackageInfo.getReceiverDispatcher(
	                    receiver, context, scheduler,
	                    mMainThread.getInstrumentation(), true);
		            } else {
		                if (scheduler == null) {
		                    scheduler = mMainThread.getHandler();
		                }
		                //假如mPackageInfo或context为空,则直接新建ReceiverDispatcher对象
		                rd = new LoadedApk.ReceiverDispatcher(
		                        receiver, context, scheduler, null, true).getIIntentReceiver();
		            }
	        }
	        // 从这里调用AMS的registerReceiver方法
             final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            ...
}

接下来看看Ams的registerReceiver方法:

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
...
	synchronized(this) {	
	...
      callerApp = getRecordForAppLocked(caller);//请求者的Activity所在的应用进程
       ...
       //迭代IntentFilter获得此次注册的action列表  
      Iterator<String> actions = filter.actionsIterator();       
      //此次注册没有action
       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) };
            //根据actions和userIds获得所有粘性广播的intent
            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>();
                            }
                            //stickyIntent中加入所有粘性广播
                            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 intent has scheme "content", it will need to acccess
                // provider that needs to lock mProviderMap in ActivityThread
                // and also it may need to wait application response, so we
                // cannot lock ActivityManagerService here.
                // 匹配此次注册的广播中是否有粘性广播
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    //此次注册的广播里有粘性广播,加入到allSticky队列里
                    allSticky.add(intent);
                }
            }
        }
        ...
         //获取 接收者队列  ReceiverList
         //mRegisteredReceivers是个map集合,可以根据IIntentReceiverd的IBinder来取出对应的ReveiverList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
         
            //如果rl为空则新建,用来存储广播接收者
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
        ...
        	//新创建的接收者队列,添加到已注册广播队列。
                mRegisteredReceivers.put(receiver.asBinder(), rl);
		...
		 	 //BroadcastFilter描述注册的广播接收者
         	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);
                //BroadcastFilter加入到ReceiverResolver,
                //ams收到广播时,可以在mReceiverResolver在ReceiverList找到对应的接收者
            }
            //所有匹配该filter的sticky广播执行入队操作
        	//如果没有使用sendStickyBroadcast,则allSticky=null。
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                     //根据intent返回前台或后台广播队列
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    //创建BroadcastRecord
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, false, null, null, OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                    //该广播加入到并行广播队列
                    queue.enqueueParallelBroadcastLocked(r);
                    //调度广播,发送BROADCAST_INTENT_MSG消息,触发处理下一个广播。
                    queue.scheduleBroadcastsLocked();
                }
            }
        ...
	}
...
}

在AMS的registerReceiver方法里可以对动态广播注册有个基本的了解,再来用文字理清晰一下,
1)把注册的粘性广播存放在allSticky队列中,
2)新建或获取此次注册广播进程的接收者队列对象rl(ReceiverList)
3)rl加入在AMS维护的mRegisteredReceivers队列中(RegisteredReceivers存放所有进程的广播接收者和对应进程信息)。
4) 新建一个BroadcastFilter对象bf描述注册的广播接收者(里面存有目的intentFilter,目的关联的接收者队列rl,注册广播的进程信息,监听广播所需要的权限等)。
5)判断rl内是否已经已经有加入过和这个bf包含的intentFilter(为了防止重复注册广播),若无,则将bf加入rl队列(ReceiverList)和 加入mReceiverResolver队列。
6)对于粘性广播,先根据intent的标识判断是前台广播还是后台广播,找到要加入的前台或后台广播队列
7)然后为注册的粘性广播新建一个BroadcastRecord(内存放该广播的前后台队列和intent)
8) 把注册的粘性广播加入并行广播队列
9)调度粘性广播,发送BROADCAST_INTENT_MSG消息,触发处理下一个广播。

至此广播的注册告一段落,接下来开始介绍广播的发送和接收~~~~

广播的发送

先以无序广播作为分析的案例进行走读

看看代码的主要时序图:

在这里插入图片描述
先歇停下,未完待续。。

猜你喜欢

转载自blog.csdn.net/qq_37977070/article/details/107923398