笔记:BroadcastReceiver的运行过程
广播概述
- 广播用来在组件之间传递消息,可以是同进程或跨进程。
- 广播机制是基于发布订阅的事件驱动模型,使用上比Binder通信(跨进程接口回调)更低耦合、简单。
- ActivityManagerService(简称AMS)作为广播消息发布订阅的注册中心,广播接收器(Broadcast Receiver,简称Receiver)以静态或动态方式注册到AMS。
- 广播底层实现就是Binder,包括使用Binder进行跨进程回调接口注册,发送广播时使用Binder异步通信发送广播给接收者所在进程。
- 继承Context的Service或Activity组件可以发送有序或无需广播到AMS。
- AMS把消息发送给接收此广播类型的Receiver。
- 有序广播根据Receiver优先级被接收,动态注册的先收到消息,而无需广播同时发送给所有Receiver。
- 广播的生命周期:动态注册的广播组件其生命周期和其使用者关联。静态注册的广播,每次收到广播时一个Receiver被创建,在主线程中执行其onReceive()方法,方法返回后,Receiver组件即等待销毁。
- 因为onReceive在主线程执行,所以耗时操作会引起ANR。
- 耗时操作应该启动一个Service去执行,不能是bindService()这样的,因为bindService()本身是和Service进行通信的方式,而不是增加Receiver存活时间的方式。onReceive()本身的执行就应该很短。startService()保证一个耗时操作放在Service中得已运行更长的时间得到执行。
NOTE:
使用Broadcast完成组件间的事件通知,在跨进程的情况下,比使用Binder进行跨进程接口回调要简单且更加低耦合。
案例
下面以在MyActivity中注册MyReceiver为例,MyReceiver接收Action为“com.hxw.bot.broadcast.ACTION”。
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
}
};
IntentFilter filter = new IntentFilter("com.hxw.bot.broadcast.ACTION");
registerReceiver(receiver, filter);
一个filter可以拦截多个Action。
广播注册过程
1. ContextWrapper.registerReceiver
2. ContextImpl.registerReceiver
LoadedApk mPackageInfo;
ActivityThread mMainThread;
...
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, filter, broadcastPermission,
scheduler, getOuterContext());
}
...
private Intent registerReceiverInternal(BroadcastReceiver receiver,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
...
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(),
rd, filter, broadcastPermission);
...
}
mMainThread.getHandler()返回一个当前进程的主线程上的Handler,
scheduler用来将AMS回调rd方法从Binder线程池的线程中转到主线程中进行。
LoadedApk.getReceiverDispatcher()将MyReceiver包装成一个IIntentReceiver rd,
rd类型是InnerReceiver(继承自IIntentReceiver.Stub)——一个Binder对象,发送给AMS其代理,完成“广播接收器”回调接口注册。
参数context为ContextImpl.getOuterContext(),它返回MyActivity对象。也就是注册MyReceiver的
Context对象,MyReceiver和MyActivity关联。
LoadedApk类有一个字段mReceivers:
private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
= new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
它以Receiver关联的Context对象(也就是执行注册的context对象)作为key,存储了对应context注册的所有的BroadcastReceiver对象。
class ReceiverDispatcher {
final IIntentReceiver.Stub mIIntentReceiver; // 作为跨进程接口实例的Binder对象
final BroadcastReceiver mReceiver; // MyReceiver
final Context mContext; // MyActivity对象
final Handler mActivityThread; // 主线程的handler
...
}
3. ActivityManagerProxy.registerReceiver
public Intent registerReceiver(IApplicationThread caller,
IIntentReceiver receiver,
IntentFilter filter, String perm) throws RemoteException
将参数写入Parcel data,然后向AMS发起进程间通信REGISTER_RECEIVER_TRANSACTION。
4. AMS.registerReceiver
AMS.registerReceiver()响应REGISTER_RECEIVER_TRANSACTION。
ActivityManagerService {
/**
* Keeps track of all IIntentReceivers that have been registered for
* broadcasts. Hash keys are the receiver IBinder, hash value is
* a ReceiverList.
*/
final HashMap mRegisteredReceivers = new HashMap();
public Intent registerReceiver(IApplicationThread caller,
IIntentReceiver receiver, IntentFilter filter,
String requiredPermission) throws RemoteException {
...
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
...
BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
rl.add(bf);
mReceiverResolver.addFilter(bf);
}
}
参数receiver:
AMS收到的“广播接收器”是MyReceiver对应的InnerReceiver的BinderProxy。
AMS使用BroadcastFilter记录已经注册的Receiver:
class BroadcastFilter extends IntentFilter {
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
final String requiredPermission;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
String _requiredPermission) {
super(_filter);
receiverList = _receiverList;
requiredPermission = _requiredPermission;
}
}
BroadcastFilter保存了关联的filter、receiverList。
class ReceiverList extends ArrayList<BroadcastFilter>
implements IBinder.DeathRecipient {
final ActivityManagerService owner;
public final IIntentReceiver receiver;
...
}
ReceiverList记录了某个Receiver——IIntentReceiver对象所接收的所有广播。
因为一个MyReceiver这样的对象可以用来同时接收多种广播类型。
AMS最后使用mReceiverResolver来根据发送的广播对应的IntentFilter找到合适的receiver并
调用。
广播发送过程
在Service或Activity中,这里假设是MyService中,通知MyActivity更新进度:
int progress = 1;
...
Intent intent = new Intent("com.hxw.bot.broadcast.ACTION");
intent.putExtra("progress", progress);
sendBroadcast(intent);
阶段1:发送广播消息给AMS
广播发送者,即Activity或Service组件,将一个特点类型的广播发送给AMS。
这里是MyActivity,发送Action为"com.hxw.bot.broadcast.ACTION"的广播。
Step0:Service.sendBroadcast
广播使用intent对象描述,其Action即广播的类型,intent也可以携带必要的数据。
Step1:ContextWrapper.sendBroadcast
Step2:ContextImpl.sendBroadcast
@Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, false);
} catch (RemoteException e) {
}
}
Step3:ActivityManagerProxy.broadcastIntent
打包参数,向AMS发起进程间通信:
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
阶段2:AMS找到接收者,通知其线程的消息队列处理广播
AMS收到一个广播后,找到与这个广播对应的接收者,将它们添加到广播调度队列。然后向创建AMS的线程的消息队列发送一个类型为BROADCAST_INTENT_MSG的消息。对于广播发送者来说,一个广播发送完成了。
AMS.unbroadcastIntent:同步和消息队列?
广播发送者向AMS发起的BROADCAST_INTENT_TRANSACTION操作是同步RPC,响应方法应该尽快返回。
对于Binder-IPC通信,AMS.unbroadcastIntent()的执行是在Binder线程中的,Binder线程一般也应该尽快执行完毕。
一个进程对应的Binder线程不止一个,所以AMS.unbroadcastIntent()是同步的,它将将广播的处理转为AMS被创建时的线程中消息队列对消息的处理,自身不执行广播的发送。
广播的发送是异步的,发送者不会等待AMS实际将广播发送给接收者操作完成。
阶段3:AMS消息队列处理BROADCAST_INTENT_MSG
当AMS所运行线程的消息队列中BROADCAST_INTENT_MSG消息被处理时,AMS从广播调度队列中找到需要接收此广播的广播接收者,并将对应的广播发送给它们所运行在的应用程序进程。
AMS.performReceiveLocked
static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky);
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
}
}
AMS发送给目标进程广播时,采用异步进程间通信方式。
发送给一个Binder对象的所有异步事务都保存在一个异步事务队列中,其中的事务每次只处理一个,就是队列头部的异步事务。所以,AMS发送给同一个应用程序进程的所有广播都可以被按照发送顺序来串行地接收和处理。
ApplicationThreadProxy.scheduleRegisteredReceiver():
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
阶段4:接收者进程在主线程消息队列中响应广播
广播接收者所运行在的应用程序进程收到AMS发送的广播后,并不是直接将收到的广播分发给MyReceiver处理,而将广播封装为一个消息,发送到主线程的消息队列中。消息被处理时,应用程序进程在主线程中将消息所描述的广播发送给相应的广播接收者MyReceiver。
ApplicationThread.scheduleRegisteredReceiver
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky) throws RemoteException {
receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}
参数receiver就是注册过程提供的InnerReceiver(见LoadedApk.java)对象。
接收者所在线程(见ActivityThread.java)将intent所表示的广播封装为一个消息(android.os.Message),然后发送到主线程消息队列中。
Args.run
final class LoadedApk {
...
static final class ReceiverDispatcher {
final IIntentReceiver.Stub mIIntentReceiver; // 作为跨进程接口实例的Binder对象
final BroadcastReceiver mReceiver; // MyReceiver
final Context mContext; // MyActivity对象
final Handler mActivityThread; // 主线程的handler
...
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
...
}
final class Args implements Runnable {
private Intent mCurIntent;
...
public void run() {
IActivityManager mgr = ActivityManagerNative.getDefault();
...
BroadcastReceiver receiver = mReceiver;
...
receiver.onReceive(mContext, intent);
...
mgr.finishReceiver(mIIntentReceiver,
mCurCode, mCurData, mCurMap, false);
}
}
}
}
这里,主线程执行Args.run()方法,得到关联的MyReceiver调用其onReceive()。
如果当前广播是有序广播,那么onReceive()执行完毕后调用mgr.finishReceiver()
通知AMS将广播传递给下一个接收者。
补充
- Binder线程池
- Binder异步通信
- sticky粘性广播
接收者可以接收到对应类型的它注册前的最后一个广播。
(本文使用Atom编写)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人