Android广播的注册和发送原理(基于Android S)

Android广播机制采用类似于“发布-订阅”者的设计模式,基于Binder机制,实现了一对多的通信。

从整体的角度看,广播的使用主要设计两个“动作” ,“注册广播”和“发送广播”,以及三个“角色”,发送方,接收方和中间人。为什么需要中间人,因为有了中间人的存在,就可以减少发送方和接收方的工作而不用去关心具体的实现细节。三者关系可参考下图:

AMS就是上面提到的中介,也是最重要的角色。Receiver向AMS注册自己感兴趣的广播,AMS则必须将所有必要的信息记录下来,比如Receiver的一些必要信息以及注册的广播的信息,统统都以一定的方式保存起来。当Sender发送广播后,AMS需要根据广播的信息从数据容器中找到注册该广播的Receiver的相关信息。此外AMS还要对很多特殊情况进行处理,和广播是否被及时处理,如果超时会产生anr,这些工作都是由AMS去做的。

一、广播的注册

如上文所说,广播的注册最终是由AMS去完成的,接收方只需要准备好一些必要的信息,如接收者和感兴趣的广播等,并将这些信息提供给AMS,最终由AMS去维护这些信息。

这里列出AMS从Receiver接收到的参数或者说是信息:

IApplicationThread callerReceiver所在的主线程的ApplicationThread
String callerPackageReceiver所在的包名
String callerFeatureIdapp访问受保护数据的属性标签
String receiverIdReceiver的id
IIntentReceiver receiverInnerReceiver对象,ReceiverDispatcher的内部类
IntentFilter filterIntent过滤器,用来匹配Intent的values
String permission广播权限
int userIdmUser.getIdentifier,ContextImpl持有mUser
int flags用来判断对InstantApp的可见性

接下来,看下AMS怎么进行注册的:

这里只关注核心逻辑,很多根据部分参数进行的特殊情况处理在这里不进行分析,粘性广播也不分析。

/*AMS*/public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,String callerFeatureId, String receiverId, IIntentReceiver receiver,IntentFilter filter, String permission, int userId, int flags) {synchronized (this) {ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {//rl为空,说明当前调用端(客户端)还没有注册过广播rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);if (rl.app != null) {//ReceiverList持有ProcessRecord对象app,app持有ProcessReceiverRecord对象mReceivers,mReceivers内部维护一个//里边存放着从当前应用注册的所有IIntentReceiverfinal int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();//当前应用可注册的广播数目超过了阈值,则抛出异常if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {throw new IllegalStateException("Too many receivers, total of "+ totalReceiversForApp + ", registered for pid: "+ rl.pid + ", callerPackage: " + callerPackage);}//这个关系链是这样的:ReceiverList  ---> ProcessRecord ---> ProcessReceiverRecord --->  ArraySet For Dynamic broadcast");}mReceiverResolver.addFilter(bf);
}

这里要关注两个变量,mRegisteredReceivers和mReceiverResolver:

/*** Keeps track of all IIntentReceivers that have been registered for broadcasts.* Hash keys are the receiver IBinder, hash value is a ReceiverList.*/
@GuardedBy("this")
final HashMap mRegisteredReceivers = new HashMap<>();

可以看出,mRegisterReceivers是AMS持有的一个HashMap,key是IIntentReceiver,value是ReceiverList,ReceiverList继承自ArrayList并封装了一些关于Receiver的属性。

/*** Resolver for broadcast intents to registered receivers.* Holds BroadcastFilter (subclass of IntentFilter).*/
final IntentResolver mReceiverResolver= new IntentResolver() {@Overrideprotected boolean allowFilterResult(BroadcastFilter filter, List dest) {IBinder target = filter.receiverList.receiver.asBinder();for (int i = dest.size() - 1; i >= 0; i--) {if (dest.get(i).receiverList.receiver.asBinder() == target) {return false;}}return true;}@Overrideprotected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL|| userId == filter.owningUserId) {return super.newResult(filter, match, userId);}return null;}@Overrideprotected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {return input;}@Overrideprotected BroadcastFilter[] newArray(int size) {return new BroadcastFilter[size];}@Overrideprotected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {return packageName.equals(filter.packageName);}
};

IntentResolver是一个抽象类,是一个对Intent的解析器,它持有一个ArraySet用来存放filters,mReceiverResolver.addFilter(bf); 就是将bf放到了mReceiverResolver持有的ArraySet中。

二、广播的发送

同样的,广播的发送的重头戏也是在AMS,由于AMS的方法动辄近千行,这里还是把AMS收到的参数都列出来,然后看看AMS要做什么。

下面是sendBroadcast(Intent intent),也就是说普通广播,传给AMS的参数:

IApplicationThread caller
发送方主线程的ApplicationThread
String callingFeatureId
app访问受保护数据的属性标签
Intent intent
用于发送广播的intent
String resolvedType
intent的MIME数据类型(不用太关注)
IIntentReceiver resultTo
null,IntentReceiver的接口
int resultCode
Activity.RESULT_OK(-1)
String resultData
null
Bundle resultExtras
null
String[] requiredPermissions
null
String[] excludedPermissions
null
int appOp
AppOpsManager.OP_NONE()
Bundle bOptions
null,携带的一些数据
boolean serialized
fasle,是否是有序广播
boolean sticky
false,是否是粘性广播
int userId
getUserId()

下面开始分析AMS的代码,同样的,跳过粘性广播。

...List registeredReceivers = null;...registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);

这里可以注意到mReceiverResolver这个变量,在广播注册时就碰见过。当时是把BroadcastFilter放到了mReceiverResolver持有的ArraySet中,我们现在大胆猜测现在要把BroadcastFilter取出来,看看queryIntent方法验证下!

public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly,int userId) {...ArrayList finalList = new ArrayList();...FastImmutableArraySet categories = getFastIntentCategories(intent);if (firstTypeCut != null) {buildResolveList(intent, categories, debug, defaultOnly, resolvedType,scheme, firstTypeCut, finalList, userId);}if (secondTypeCut != null) {buildResolveList(intent, categories, debug, defaultOnly, resolvedType,scheme, secondTypeCut, finalList, userId);}if (thirdTypeCut != null) {buildResolveList(intent, categories, debug, defaultOnly, resolvedType,scheme, thirdTypeCut, finalList, userId);}if (schemeCut != null) {buildResolveList(intent, categories, debug, defaultOnly, resolvedType,scheme, schemeCut, finalList, userId);}filterResults(finalList);sortResults(finalList);if (debug) {Slog.v(TAG, "Final result list:");for (int i=0; i

这里根据intent拿到匹配到的BroadcastFilter的List,接下来就开始处理这些BroadcastFilter。

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {// If we are not serializing this broadcast, then send the// registered receivers separately so they don't wait for the// components to be launched.if (isCallerSystem) {checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,isProtectedBroadcast, registeredReceivers);}final BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers,resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,allowBackgroundActivityStarts, backgroundActivityStartsToken,timeoutExempt);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);final boolean replaced = replacePending&& (queue.replaceParallelBroadcastLocked(r) != null);// Note: We assume resultTo is null for non-ordered broadcasts.if (!replaced) {queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();}registeredReceivers = null;NR = 0;
}

大家自己看吧,这里再看下enqueueParallelBroadcastLocked(r)和scheduleBroadcastsLocked()

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {mParallelBroadcasts.add(r);enqueueBroadcastHelper(r);
}public void scheduleBroadcastsLocked() {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["+ mQueueName + "]: current="+ mBroadcastsScheduled);if (mBroadcastsScheduled) {return;}mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;
}
final BroadcastHandler mHandler;
private final class BroadcastHandler extends Handler {public BroadcastHandler(Looper looper) {super(looper, null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case BROADCAST_INTENT_MSG: {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["+ mQueueName + "]");processNextBroadcast(true);} break;case BROADCAST_TIMEOUT_MSG: {synchronized (mService) {broadcastTimeoutLocked(true);}} break;}}
}
private void processNextBroadcast(boolean fromMsg) {synchronized (mService) {processNextBroadcastLocked(fromMsg, false);}
}
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {BroadcastRecord r;...// First, deliver any non-serialized broadcasts right away.while (mParallelBroadcasts.size() > 0) {r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),System.identityHashCode(r));Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),System.identityHashCode(r));}final int N = r.receivers.size();if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["+ mQueueName + "] " + r);for (int i=0; i

到这吧,如果有观众看到这,自己往下看吧。 

三、总结

本文试图从源码分析广播的注册和发送的原理,奈何源码实在有点庞大,讲的太细根本不可能。我自己也意识到,可能自己阅读源码的方式有问题。不能为了看源码而去看源码,而应该带着自己的问题去阅读源码,只要阅读之后能回答自己的问题就是值得的,就是有收获的。以后就这么看。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章