4.4 广播的注册、发送和接收过程
广播作为四大组件之一,使用频率远没有Activity高,但是广播的工作过程还是十分有必要了解的。本节主要从三个方面讲解广播工作过程,分别是广播的注册、发送和接收,这些过程和本章前3节重叠的部分,这一节不会再赘述,而是一笔带过,建议阅读本节前先阅读本章前面3节的内容。
4.4.1 广播的注册过程
广播的注册通俗来讲就是广播接收者注册自己感兴趣的广播,广播的注册分为两种,分别是静态注册和动态注册,静态注册在应用安装时由PackageManagerService来完成注册过程,关于这一过程本节不做介绍,这里只介绍广播的动态注册,时序图如图4-12所示。
图4-12 广播的动态注册过程时序图
要想动态注册广播,需要调用registerReceiver方法,它在ContextWrapper中实现,代码如下所示:
这里mBase具体指向就是ContextImpl,ContextImpl的registerReceiver方法有很多重载的方法最终会调用registerReceiverInternal方法:
在注释1处判断如果LoadedApk类型的mPackageInfo不等于null,并且context不等于null就调用注释2处的代码,通过mPackageInfo的getReceiverDispatcher方法获取rd对象,否则就调用注释3处的代码来创建rd对象。注释2和注释3处的代码的目的都是要获取IIntentReceiver类型的rd对象,IIntentReceiver是一个Binder接口,用于广播的跨进程的通信,它在LoadedApk.ReceiverDispatcher.InnerReceiver中实现,如下所示:
回到registerReceiverInternal方法,在注释4处调用了IActivityManager的registerReceiver方法,最终会调用AMS的registerReceiver方法,并将IIntentReceiver类型的rd传进去,这里之所以不直接传入BroadcastReceiver而是传入IIntentReceiver,是因为注册广播是一个跨进程过程,需要具有跨进程的通信功能的IIntentReceiver。registerReceiver方法内容比较多,这里分为两个部分来进行讲解,先来查看registerReceiver方法的part1,如下所示:
1.registerReceiver方法的part1
在注释1处通过getRecordForAppLocked方法得到ProcessRecord类型的callerApp对象,它用于描述请求AMS注册广播接收者的Activity所在的应用程序进程。在注释2处根据传入的IntentFilter类型filter得到actions列表,根据actions列表和userIds(userIds可以理解为应用程序的uid)得到所有的粘性广播的intent,并在注释3处传入到stickyIntents中。接下来从stickyIntents中找到匹配传入的参数filter的粘性广播的intent,在注释4处将这些intent存入到allSticky列表中,从这里可以看出粘性广播是存储在AMS中的。
2.registerReceiver方法的part2
接下来查看AMS的registerReceiver方法的剩余内容,如下所示:
在注释1处获取ReceiverList列表,如果为空则在注释2处创建,ReceiverList继承自ArrayList,用来存储广播接收者。在注释3处创建BroadcastFilter并传入此前创建的ReceiverList,BroadcastFilter用来描述注册的广播接收者,并在注释4处通过add方法将自身添加到ReceiverList中。在注释5处将BroadcastFilter添加到IntentResolver类型的mReceiverResolver中,这样当AMS接收到广播时就可以从mReceiverResolver中找到对应的广播接收者了,从而达到了注册广播的目的。
4.4.2 广播的发送和接收过程
广播的发送和接收过程分为两个部分来进行讲解,分别是ContextImpl到AMS的调用过程和AMS到BroadcastReceiver的调用过程。
4.4.2.1 ContextImpl到AMS的调用过程
广播可以发送多种类型,包括无序广播(普通广播)、有序广播和粘性广播,这里以无序广播为例来讲解广播的发送过程。要发送无序广播需要调用sendBroadcast方法,它同样在ContextWrapper中实现,按照惯例先给出ContextImpl到AMS的调用过程的时序图,如图4-13所示。
图4-13 ContextImpl到AMS的调用过程的时序图
先来查看ContextWrapper的sendBroadcast方法,如下所示:
接着来看ContextImpl中的sendBroadcast方法,如下所示:
又是熟悉的代码,最终会调用AMS的broadcastIntent方法:
我们先来查看注释1处的verifyBroadcastLocked方法:
verifyBroadcastLocked 方法主要验证广播是否合法,在注释1处验证intent是否不为null并且有文件描述符。注释2处获得intent中的flag。注释3处如果系统正在启动过程中,判断如果flag设置为FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(启动检查时只接受动态注册的广播接收者)则不做处理,如果不是则在注释4处判断如果flag没有设置为FLAG_RECEIVER_REGISTERED_ONLY(只接受动态注册的广播接收者)则会抛出异常。我们再回到broadcastIntent方法,在注释2处调用了broadcastIntentLocked方法,代码如下所示:
这里省略了很多代码,前面的工作主要是将动态注册的广播接收者和静态注册的广播接收者按照优先级高低不同存储在不同的列表中,再将这两个列表合并到receivers列表中,这样receivers列表包含了所有的广播接收者(无序广播和有序广播)。在注释1处创建BroadcastRecord对象并将receivers传进去,在注释2处调用BroadcastQueue的scheduleBroadcastsLocked方法。
4.4.2.2 AMS 到BroadcastReceiver的调用过程
AMS到BroadcastReceiver的调用过程的时序图如图4-14所示。
图4-14 AMS到BroadcastReceiver的调用过程的时序图
BroadcastQueue的scheduleBroadcastsLocked方法的代码如下所示:
在注释1处向BroadcastHandler类型的mHandler对象发送了BROADCAST_INTENT_MSG类型的消息,这个消息在BroadcastHandler的handleMessage方法中进行处理,如下所示:
在handleMessage方法中调用了processNextBroadcast方法,方法对无序广播和有序广播分别进行处理,旨在将广播发送给广播接收者,下面给出processNextBroadcast方法中对无序广播的处理部分:
从前面BroadcastHandler方法中我们得知传入的参数fromMsg的值为true,因此在注释1处将mBroadcastsScheduled设置为flase,表示对于此前发来的BROADCAST_INTENT_MSG类型的消息已经处理了。注释2处的mParallelBroadcasts列表用来存储无序广播,通过while循环将mParallelBroadcasts 列表中的无序广播发送给对应的广播接收者。在注释3处获取每一个mParallelBroadcasts列表中存储的BroadcastRecord类型的r对象。在注释4处将这些r对象描述的广播发送给对应的广播接收者,deliverToRegisteredReceiverLocked方法如下所示:
这里省去了大部分的代码,这些代码是用来检查广播发送者和广播接收者的权限的。如果通过了权限的检查,则会调用注释1处的performReceiveLocked方法:
在注释1和注释2处的代码表示如果广播接收者所在的应用程序进程存在并且正在运行,则执行注释3处的代码,表示用广播接收者所在的应用程序进程来接收广播,这里app.thread指的是ApplicationThread,我们来查看ApplicationThread 的scheduleRegisteredReceiver 方法,代码如下所示:
在scheduleRegisteredReceiver 方法中调用了IIntentReceiver类型的对象receiver的performReceive方法,IIntentReceiver在前面提到过,用于广播的跨进程的通信,它的具体实现为LoadedApk.ReceiverDispatcher.InnerReceiver,代码如下所示:
IIntentReceiver和IActivityManager 一样,都使用了AIDL来实现进程间通信。InnerReceiver继承自IIntentReceiver.Stub,是Binder通信的服务器端,IIntentReceiver则是Binder 通信的客户端、InnerReceiver 在本地的代理,它的具体的实现就是InnerReceiver。在InnerReceiver的performReceive方法的注释1处调用了ReceiverDispatcher类型的rd对象的performReceive方法,如下所示:
在注释1处将广播的intent等信息封装为Args对象,在注释2处调用mActivityThread的post方法并传入了Args对象。这个mActivityThread是一个Handler对象,具体指向的就是H,注释2处的代码就是将Args对象的getRunnable方法通过H发送到线程的消息队列中,Args的getRunnable方法如下所示:
在注释1处执行了BroadcastReceiver类型的receiver对象的onReceive方法,这样注册的广播接收者就收到了广播并得到了intent。