简介
广播作为Android系统四大组件之一,起得作用是相当大,安卓系统进程之间通信是相当频繁,而且解耦工作是重中之重,那么作为这样的设计初衷,所以诞生了广播。我们今天就来一步步看看安卓中广播是怎么样进行工作的。
使用
- 自定义广播接受者
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Log.i(TAG, msg);
}
}
- 注册广播
- 静态注册
- 动态注册
MyReceiver receiver = new MyReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.MY_BROADCAST"); registerReceiver(receiver, filter);
- 发送广播
public void send(View view) {
Intent intent = new Intent("android.intent.action.MY_BROADCAST");
intent.putExtra("msg", "hello receiver.");
sendBroadcast(intent);
}
- 解除广播
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
补充:
有序广播的定义案例:
源码分析
注册广播源码分析
我们通过ContextImpl对象最后会进入如下代码:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
//mPackageInfo的类型为LoadedApk
if (mPackageInfo != null && context != null) {
//类型是Handler,没有设置的时候用主线程和处理
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 {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mPackageInfo.getReceiverDispatcher()
这个方法主要在LoadedApk的private final ArrayMap
根据BroadcastReceiver r
拿到LoadedApk.ReceiverDispatcher
如果没有,则new ReceiverDispatcher
将信息封装到ReceiverDispatcher
中然后保存到数组中,当new ReceiverDispatcher
会创建(mIIntentReceiver = new InnerReceiver(this, !registered);
这个是一个binder)
反正上面代码执行完成就要有LoadedApk.ReceiverDispatcher的创建,并且要调用AMS的registerReceiver()
同样在new ReceiverDispatcher的时候就会创建IIntentReceiver对象,这个对象是继承Binder的。
在使用广播的时候,我们就会遇到一种情况,就是广播导致的anr,由于上面handler是主线程的handler,所在在oReceiver的时候,一旦超时就会引发anr问题,我们可能会想和service处理办法一样,由于service的onCreat也是在主线程中调用的,我们处理会启动一个子线程,在子线程中完成我们需要的工作。同理我们可不可以在onReceive方法中启动子线程呢?
答案是不可以!如果我们用了子线程可能会产生这样一种情况,onReceive的生命周期很短可能我们在子线程中的工作没有做完,AMS就销毁进程了,这个时候就达不到我们需要的结果。
所以广播也给我们准备了一种办法,就是PendingResult,它是定义在BroadcastReceiver中的:
public abstract class BroadcastReceiver {
private PendingResult mPendingResult;
具体的做法是:
- 先调用BroadcastReceiver的goAsync函数得到一个PendingResult对象,
- 然后将该对象放到工作线程中去释放。
这样onReceive函数就可以立即返回而不至于阻塞主线程。
同时,Android系统将保证BroadcastReceiver对应进程的生命周期,
直到工作线程处理完广播消息后,调用PendingResult的finish函数为止。
private class MyBroadcastReceiver extends BroadcastReceiver {
..................
public void onReceive(final Context context, final Intent intent) {
//得到PendingResult
final PendingResult result = goAsync();
//放到异步线程中执行
AsyncHandler.post(new Runnable() {
@Override
public void run() {
handleIntent(context, intent);//可进行一些耗时操作
result.finish();
}
});
}
}
final class AsyncHandler {
private static final HandlerThread sHandlerThread = new HandlerThread("AsyncHandler");
private static final Handler sHandler;
static {
sHandlerThread.start();
sHandler = new Handler(sHandlerThread.getLooper());
}
public static void post(Runnable r) {
sHandler.post(r);
}
private AsyncHandler() {}
}
细节对应到源码
public final PendingResult goAsync() {
PendingResult res = mPendingResult;
mPendingResult = null;
return res;
}
在onReceive函数中执行异步操作,主要目的是避免一些操作阻塞了主线程,
但整个操作仍然需要保证在10s内返回结果,尤其是处理有序广播和静态广播时。
毕竟AMS必须要收到返回结果后,才能向下一个BroadcastReceiver发送广播。
源码过程
ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
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 {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
首先传递进来的核心参数:
- BroadcastReceiver receiver
- Context context
- IntentFilter filter
此方法第一步:
- scheduler = mMainThread.getHandler();//获取主线程
- 获取IIntentReceiver,是将receiver封装的对象
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
- 最后通过AMS的registerReceiver将信息注册到AMS中
我们分析一下这个IIntentReceiver rd
到底是什么?
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
这里面的几个核心数据结构:
- mReceivers
private final ArrayMap
> mReceivers; - map
ArrayMap
map; - rd
rd = new ReceiverDispatcher(r, context, handler, instrumentation, registered);
由上面代码可以看出来是:
- 将BroadcastReceiver(r)封装到ReceiverDispatcher(rd)
- 将r和rd对应起来到一个map
- context和map对应起来
最后形成的效果是:
ArrayMap>
方法最后返回final IIntentReceiver.Stub mIIntentReceiver;
这个是在ReceiverDispatcher的构造中进行创建的,传入的参数是ReceiverDispatcher这个对象本身,也就是说receiver,context等等所有的引用都在这里面。同时它是一个Binder对象,说明可以夸进程传输数据。
上面的信息说明,在getReceiverDispatcher这个方法中将receiver,context等等都保存起来,并且统一在LoadedApk列表中进行保存。于此同时返回一个Binder对象给调用者(注册广播的对象)
小节:
到这里我们明白ContextImpl对象的registerReceiverInternal方法的目的是将信息保存到LoadedApk中,并且生成Binder对象这个对象中存有所有recevire和context等重要引用,最后AMS就可以通过这个Binder对象的引用和当前注册广播的进程进行通信。
AMS.registerReceiver()
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
part-1
...............
//保存系统内已有的sticky广播
ArrayList stickyIntents = null;
...............
synchronized(this) {
if (caller != null) {
//根据IApplicationThread得到对应的进程信息
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
//抛出异常,即不允许未登记的进程注册BroadcastReceiver
...............
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
//抛出异常,即注册进程必须携带Package名称
..................
}
} else {
.............
}
......................
//为了得到IntentFilter中定义的Action信息,先取出其Iterator
Iterator actions = filter.actionsIterator();
if (actions == null) {
ArrayList noAction = new ArrayList(1);
noAction.add(null);
actions = noAction.iterator();
}
//可能有多个action,所以这里得到action的迭代器actions
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
//依次比较BroadcastReceiver关注的Action与Stick广播是否一致
String action = actions.next();
for (int id : userIds) {
ArrayMap> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
//Sticky广播中,有BroadcastReceiver关注的
//可能有多个Intent,对应的Action相似,在此先做一个初步筛选
ArrayList intents = stickies.get(action);
if (intents != null) {
//如果stickyIntents==null,则创建对应列表
if (stickyIntents == null) {
stickyIntents = new ArrayList();
}
//将这些广播保存到stickyIntents中
stickyIntents.addAll(intents);
}
}
}
}
}
ArrayList allSticky = null;
//stickyIntents中保存的是action匹配的
if (stickyIntents != null) {
//用于解析intent的type
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);
//此时进一步判断Intent与BroadcastReceiver的IntentFilter是否匹配
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList();
}
allSticky.add(intent);
}
}
}
第一部分主要工作是:
- 得到action的迭代器
- 得到UserAll,当前user的所有粘连广播列表添加到sticktIntents中
- 在
stickyIntents
中进行匹配将所有对应粘连广播的intent添加到allSticky中。
part-2
//得到粘连挂广播中的第一个广播
Intent sticky = allSticky != null ? allSticky.get(0) : null;
................
synchronized (this) {
..............
//一个Receiver可能监听多个广播,多个广播对应的BroadcastFilter组成ReceiverList
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
//首次注册,rl为null,进入此分支
//新建BroadcastReceiver对应的ReceiverList
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//添加到注册进程里
rl.app.receivers.add(rl);
} else {
try {
//rl监听receiver所在进程的死亡消息
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//保存到AMS上
mRegisteredReceivers.put(receiver.asBinder(), rl);
} ..........
............
这里牵扯到一个数据结构:
- mRegisteredReceivers
final HashMap mRegisteredReceivers;
这个主要保存的是注册的广播的receiver和ReceiverList,再看看ReceiverList这个数据结构
class ReceiverList extends ArrayList
主要是存BroadcastFilter
怎么理解呢?就是一个广播接受者可以对应多个广播过滤器,也就是BroadcastFilter,所以就形成了HashMap
这种结构
这种结构也就是说,一个广播可以注册多次,每次filter不同,则会将filter添加到对应的广播接收器中。
第二部分做的工作主要有:
将filter保存到对应的BroadcastFilter中,然后将BroadcastFilter保存到对应的ReceiverList中,也就是说一个recevier对应多个Filter。
part-3
//创建当前IntentFilter对应的BroadcastFilter
//AMS收到广播后,将根据BroadcastFilter决定是否将广播递交给对应的BroadcastReceiver
//一个BroadcastReceiver可以对应多个IntentFilter
//这些IntentFilter对应的BroadcastFilter共用一个ReceiverList
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
................
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
//allSticky不为空,说明有粘性广播需要发送给刚注册的BroadcastReceiver
if (allSticky != null) {
ArrayList receivers = new ArrayList();
//receivers记录bf
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
//根据intent的flag (FLAG_RECEIVER_FOREGROUND)决定是使用gBroadcastQueue还是BgBroadcastQueue
BroadcastQueue queue = broadcastQueueForIntent(intent);
//创建广播对应的BroadcastRecord
//BroadcastRecord中包含了Intent,即知道要发送什么广播;
//同时其中含有receivers,内部包含bf,即知道需要将广播发送给哪个BroadcastReceiver
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
//加入到BroadcastQueue中,存入的是并发广播对应的队列
queue.enqueueParallelBroadcastLocked(r);
//将发送BROADCAST_INTENT_MSG,触发AMS发送广播
queue.scheduleBroadcastsLocked();
}
}
}
}
将通过intent匹配的粘连广播进行发送。
小节上面1.2.3部分
- 将系统中粘连广播根据intent进行匹配并得到保存在粘连广播列表
- 将传递进来的receiver和对应的intentfilterList对应起来保存到对应列表
- 发送匹配到的粘连广播
这里可以看出粘连广播的特性,就是如果注册是广播是粘连广播那么就在注册的时候就会给所有接受这个粘连广播的接收器发送广播。