本文为原创文章,欢迎转载!转载时请注明出处:http://blog.csdn.net/windskier
这篇文章开始从framework的角度来研究一下android四大控件最后一个控件BroadcastReceiver 的机制与原理,BroadcastReceiver 字面意思就是广播接收器,它能接收来自于系统的以及其他appliaction的广播消息,是android中非常重要的一个component。
1. BroadcastReceiver注册
BroadcastReceiver的注册有两种方式,一种是通过Context.registerReceiver()方法来进行动态注册,或者通过在AndroidManifest.xml中进行静态注册。AMS对两种不同注册方式的Receiver有不同的管理方式。静态注册就不用说了,静态注册的receiver的管理是有PMS负责的,而AMS则需要对动态注册的receiver注册和管理。下面就研究一下AMS对receiver的动态管理过程。
1.1 ReceiverDispatcher
同Service一样,LoadedApk为receiver提供了一个ReceiverDispatcher类型,类似于Service管理时的ServiceDispatcher类型,对于每个要注册的receiver,LoadedApk会为其创建一个ReceiverDispatcher。从其名称来看,ReceiverDispatcher就是向receiver dispatch广播信息,下面简单介绍一下这个类型存在的价值,广播信息是经由AMS进行分发管理的,因此需要为AMS向receive分发广播消息设计一个接口,通过这个接口可以实现receiver与AMS的IPC通信,BroadcastReceiver 本身并不是一个IBinder接口,因此需要单独设计一个,这个接口就是InnerReceiver,InnerReceiver一方面与AMS进行IPC通信,另一方面还需要与receiver进行交互,为了区分IPC的接口方法和与receiver的交互接口,这里需要定义了另外一个类型ReceiverDispatcher,这就是ReceiverDispatcher产生的原因。 @ContextImpl.java
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 {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission);
} catch (RemoteException e) {
return null;
}
}
1.2 InnerReceiver
上面一节已经介绍了InnerReceiver存在的意义,它其实是一个IInterface,用于AMS和Receiver之间的IPC通信,InnerReceiver实现了接口IIntentReceiver。 @IIntentReceiver.aidl
oneway interface IIntentReceiver {
void performReceive(in Intent intent, int resultCode,
String data, in Bundle extras, boolean ordered, boolean sticky);
}
1.3 sticky broadcast
有这么一种broadcast,在发送并经过AMS分发给对应的receiver后,这个broadcast并不会被丢弃,而是保存在AMS中,当有新的需要动态注册的receiver请求AMS注册时,如果这个receiver能够接收这个broadcast,那么AMS会将在receiver注册成功之后,马上向receiver发送这个broadcast。这种broadcast我们称之为stickybroadcast。 当receiver注册成功后,AMS会将合适的sticky broadcast作为方法registerReceiver()返回给Component,以便通知Component当前是否有sticky broadcast存在。
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
如果有多个符合receiver条件的 sticky broadcast的话,那么AMS将会只返回一个时间较早的sticky broadcast,并且会将这些所有满足要求的sticky broadcast均发送给reciever,交由receiver去处理。sticky broadcast的发送于取消通过方法sendStickyBroadcast()/sendStickyOrderedBroadcast() 以及removeStickyBroadcast()来控制。 registerReceiver()@ActivityManagerService.java
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastRecord r = new BroadcastRecord(intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
false, true, true);
if (mParallelBroadcasts.size() == 0) {
scheduleBroadcastsLocked();
}
mParallelBroadcasts.add(r);
}
}
1.4 动态注册receiver的注册管理
receiver 的动态注册过程就需要掌握以上几个知识点就可以了,真正的注册名其曰注册,其实质就是将其交由 AMS 去管理,因此下面我们来分析研究一下 AMS 是通过哪些数据结构来管理 receiver 的。掌握了这些数据结构的含义以及它们之间的相互关系之后,我们就明白了整个需注册 receiver 的注册过程了。
1.4.1 BroadcastFilter
对于每个注册过程,由两部分组成,一部分是是BroadcastReceiver,另一部分是IntentFilter,BroadcastReceiver通过IntentFilter来指定要接收的Intent BroadCast。而BroadcastFilter则是继承自IntentFilter,它记录了当前需注册的Receiver的IntentFilter信息。因此每一组BroadcastReceiver和IntentFilter对应一次注册过程,每个注册过程即是一个BroadcastReceiver向AMS指示一个Intent Broadcast的需求,如果有多个Intent Broadcast需求,BroadcastReceiver需要向AMS进行多次注册。
1.4.2 ReceiverList
ReceiverList继承自Arraylist,并且该Arraylist是以BroadcastFilter为元素,同时我们知道BroadcastReceiver的注册过程的二要素是BroadcastReceiver和IntentFilter,对于同一个BroadcastReceiver可能会接收多个Intent Broadcast,因此每个IIntentReceiver可能会对应多个BroadcastFilter,这也是设计ReceiverList这个Arraylist的主要目的。
1.4.3 mReceiverResolver
mReceiverResolver的定义如下:
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
= new IntentResolver<BroadcastFilter, BroadcastFilter>()
mReceiverResolver是一个IntentResolver类型的对象,通过它AMS可以将BroadcastFilter按照不同的元素,如action、data、category,进行管理,这样的话在AMS收到broadcast之后,会根据这个broadcast的Intent的元素去query mReceiverResolver,获取对应的BroadcastFilter。
既然mReceiverResolver实现了对BroadcastFilter的管理,那么前面的ReceiverList类型存在的意义是什么呢? mReceiverResolver和ReceiverList的关系其实并不冲突,AMS可以通过mReceiverResolver来获得可以接收发来的Broadcast的所有的BroadcastFilter。通过上面的类型关系图,我们可以看出BroadcastFilter中反向链接到自己所处的ReceiverList,看见两者之间的关系并不是对立的,而是适用于AMS接收分发过程的不同阶段。
1.4.4 BroadcastRecord
就向前面介绍的android中的其他几大component一样,BroadReceiver在被处理时同样是通过一个BroadcastRecord的Record类型来进行管理的,BroadcastRecord只有在AMS在处理分发Broadcast时才去创建,它记录着每个Broadcast的信息,在Receiver的过程并不涉及Broadcast。
2. Broadcast的发送过程
上面第一节介绍了Receiver的动态注册过程,那么接下来我们分析一下Broadcast的发送处理。
2.1 Broadcast的发送方式
Broadcast根据不同的需求有多种不同的发送方式,下面来分析一下具体有几种发送方式。下面的表格列出了application几种不同的发送方式以及不同方式对应的方法。
不同的发送方式 | 对应的方法 |
普通模式 | sendBroadcast(Intent intent) |
Broadcast sender 指定了receiver的permission,只有符合这个permission的receiver才能接收这个Broadcast | sendBroadcast(Intent intent, String receiverPermission) |
要求符合要求的所有receiver (包括动态注册和静态注册的receiver)按照优先级顺序来接收这个Broadcast | sendOrderedBroadcast(Intent intent, String receiverPermission) |
某些情况下,Broadcast sender在发出Broadcast之后,向AMS指定一个resultReceiver,期望所有的receiver接收并处理完Broadcast之后,将在各个receiver间传递的resultExtras返回给resultReceiver。这个resultExtras既可以由sender指定,也可以由某一个receiver指定,一般情况下只要receiver需要向sender返回数据,则可以通过这个resultExtras | sendOrderedBroadcast(Intent intent,String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) |
Sticky Broadcast。前面已描述 | sendStickyBroadcast(Intent intent); endStickyOrderedBroadcast(Intent intent,BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) |
2.2 Package manager Broadcast
AMS在处理收到的Broadcast时,需要对一些特殊的Broadcast做特殊处理,比如Package manager发来的关于Package移除、增加、修改等操作的,由于android系统允许user在package内部component在运行的情况下进行如上所述的操作,因此每当发生这些操作时,PM则会通过发送Broadcast的方式通知AMS进行属于AMS范畴的一些操作,如当PM移除某个Package的时候,要求AMS停止这个Package相关的所有Component等。
同样的,关于timezone的一些事件也是通过Broadcast的方式通知AMS,这里就不再分析。
2.3 Protected Broadcast
2.3.1 Protected Broadcast作用
从其名称看即是受保护的Broadcast,它声明在AndroidManifest.xml的一级节点中,对应的节点名称为<protected-broadcast>,一般情况下,protected broadcast定义的是系统级别的broadcast,通常只能被系统服务或者appliaction发送,下面我们分析那些情况下才能发送protected broadcast。
首先看一下protectedbroadcast的定义,它的主要元素就是一个action name,这些action只能被系统发送,我们可以想象一下,系统级别的broadcast可以不受限制的被所有的应用发送的后果,那将是非常严重的,不可想象的。因此从安全性的角度出发,android为了保证这些系统级别的broadcast不被第三方的应用滥发,提出了这个protected broadcast的概念。
<protected-broadcast
android:name=[action String] />
Android规定了一些可以发送protected broadcast的进程,
● android:sharedUserId="android.uid.system"
● android:sharedUserId="android.uid.phone"
● android:sharedUserId="android.uid.shell"
● 设置android:persistent的appliaction。
对于其他不符合上述四条的进程发送broadcast时,AMS回去检查该是否是protected broadcast规定的action,如果是,那么将不允许这个broadcast发送。具体代码如下:
broadcastIntentLocked()@ActivityManagerService.java
if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
|| callingUid == Process.SHELL_UID || callingUid == 0) {
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
if (AppGlobals.getPackageManager().isProtectedBroadcast(
intent.getAction())) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return BROADCAST_SUCCESS;
}
}
2.3.2 Protected Broadcast定义
上面介绍了Protected Broadcast的作用,知道这类Broadcast一般是系统发送的,那么我们再来分析一下哪些AndroidManifest.xml中可以定义Protected Broadcast。我们可容易的想到,Protected Broadcast也不是随随便便定义在一个application的AndroidManifest.xml。
只有系统appliaction才能在其AndroidManifest.xml中定义Protected Broadcast,系统appliaction包括/system/framework、/system/app、vendor/app下的package,因此设备中安装的第三方apk中如果定义了Protected Broadcast,那么这个Protected Broadcast将不生效。下面代码是对<protected-broadcast>属性的解析,可以看出解析之后会检查当前的package是否为系统package,如果是则提取这个ProtectedBroadcast。
parsePackage ()@PackageParser.java
} else if (tagName.equals("protected-broadcast")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
sa.recycle();
if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList<String>();
}
if (!pkg.protectedBroadcasts.contains(name)) {
pkg.protectedBroadcasts.add(name.intern());
}
}
XmlUtils.skipCurrentTag(parser);
}
2.4 查找receiver
这一节来分析一下AMS在收到Broadcast之后,查找匹配的receiver来向其分发这个Broadcast。前面说过receiver有两种,一种是动态注册的,另一种则是预先定义静态注册的。不同类型的receiver有不同的管理方式,动态注册的receiver从上面章节中介绍的IntentResolver mReceiverResolver中获取;静态注册的receiver则是从PM中获取。
2.5 Broadcast分发模式
2.1中介绍了几种android中常用的Broadcast发送方式,sticky模式上面的文章中已经做了介绍,下面主要介绍一些ordered模式和非ordered模式下,不同的分发模式。
2.5.1 非ordered模式
默认情况下,Broadcast发送方式是非order模式的,这里所说的ordered和非ordered模式是针对动态注册的receiver来说的,而对于静态注册的receiver则一律是ordered模式。非ordered模式下,AMS对Broadcast的分发是一种非同步的模式,也就是说,不待一个receiver处理完Broadcast,就会将Broadcast分发给下一个receiver。
2.5.2 ordered模式
ordered模式下,动态注册的receiver将会采取和静态注册的receiver相同的处理模式,类似于一种同步模式,AMS会等待一个reciever处理完这个Broadcast,才会将Broadcast分发给下一个receiver。这种模式下,receiver的处理有了先后顺序,因此必须考虑如何布置这个先后顺序,这里按照优先级的顺序来给receiver的处理顺序进行排序。
对于动态注册的receiver,可以在其IntentFilter类型中定义它的优先级;而对于静态注册的receiver,同样的我们可以在其<intent-filter>属性中定义它的优先级。根据receiver优先级从高到低进行排序,如果动态的注册的和静态注册的receive处在相同的优先级,那么将优先处理动态注册的receiver。
broadcastIntentLocked()@ActivityManagerService.java
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
2.6 Broadcast分发过程
Broadcast分发过程是一个异步过程,AMS在收到Broadcast之后,会根据不同类型的receiver,按照Broadcast发送发指定的发送模式进行分发,现在我们来分析一下Broadcast分发过程。
AMS向自己的消息队列发送消息BROADCAST_INTENT_MSG对所有的receiver进行分发操作,所有的分发操作由方法processNextBroadcast()来实现,因此这一部分我们着重分析一下这个方法中的实现过程。
2.6.1 非ordered分发
上面已经对ordered模式和非ordered模式进行了简单介绍,这里不再多介绍了。对于非ordered模式的分发过程,AMS则会将Broadcast直接分发给符合要求的receiver的进程的消息队列,这种分发的方式可以视作一种异步方式,也可以看作是将该Broadcast同时分发给符合要求的动态注册的receiver,上面章节中我们分析过只有动态注册的receiver才会有ordered和非ordered模式之分,而静态注册的reciever则无此之分,一概视为ordered模式。
2.6.2 ordered分发
满足ordered模式的receiver,AMS对其进行Broadcast分发时是一种同步的过程,只有前一个receiver处理完该Broadcast之后,并且receiver通知AMS该Broadcast已经处理结束,此时AMS才能将Broadcast分发给下一个receiver去处理,如下图所示:
我们知道需要ordered分发的receiver既包括动态注册的receiver,又包括静态注册的receiver,但是AMS向两种不同的注册方式的receiver分发Broadcast的方式有不尽相同,但是最基本的发送及处理过程是按照上面的图示进行的,两种注册方式具体的分发和处理内容如下图所示:
2.7 Broadcast处理超时
AMS向各个BroadcastReceiver分发Broadcast过程以及BroadcastReceiver处理Broadcast的过程并不是无限期的,为了保证系统的响应性和稳定性,android给这个过程设置了固定期限,有Broadcast相关的有两种固定期限,这两种期限均是针对Ordered模式的Receiver,而对于非Ordered模式的Receiver则不需要考虑这个时限,下面来一一介绍一下。
2.7.1 BroadcastRecord处理总时长
针对每个BroadcastRecord的处理,均会设置一个总时长,这个总时长是指所有符合BroadcastRecord条件的receiver处理完这个Broadcast的总时长的上限。也就是说如果满足条件的所有的Receiver均已经接收并处理完了这个Broadcast的时刻,不能超过规定的总时长,总时长跟满足条件的Receiver总个数有关系,如下面代码所示:
processNextBroadcast ()@ActivityManagerService.java
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
Slog.w(TAG, "Hung broadcast discarded after timeout failure:"
+ " now=" + now
+ " dispatchTime=" + r.dispatchTime
+ " startTime=" + r.receiverTime
+ " intent=" + r.intent
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
2.7.2 BroadcastReceiver处理时长
除了设置针对一个BroadcastRecord的处理的总时长外,android针对每个Receiver的处理时长也做了一个时限设定,保证每一个的Receiver的处理时长不超过规定时长,具体如下面代码所示:
processNextBroadcast ()@ActivityManagerService.java
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG,
"Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
}
如果上述2种时长存在超时的话,AMS将会抛出ANR, 向user报告当前的Receiver在处理Broadcast时存在无响应状况或者响应时间过长等状况。
broadcastTimeoutLocked()@ActivityManagerService.java
if (anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}