本文主要是介绍【Broadcast】sendBroadcast流程(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文基于Androd8.0。
广播发送有很多个函数,此处以最简单的sendBroadcast为例分析:
sendBroadcast实际是调用的ContextImpl的sendBroadcast:
@Overridepublic void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess(this);ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,getUserId());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
其中ActivityManger.getService():
/**
* @hide
*/
public static IActivityManager getService() {return IActivityManagerSingleton.get();
}private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}
};
经过Binder驱动,最后会调用到ActivityManagerService.broadcastIntent。
public final int broadcastIntent(IApplicationThread caller,Intent intent, String resolvedType, IIntentReceiver resultTo,int resultCode, String resultData, Bundle resultExtras,String[] requiredPermissions, int appOp, Bundle bOptions,boolean serialized, boolean sticky, int userId) {enforceNotIsolatedCaller("broadcastIntent");synchronized(this) {intent = verifyBroadcastLocked(intent);final ProcessRecord callerApp = getRecordForAppLocked(caller);final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();int res = broadcastIntentLocked(callerApp,callerApp != null ? callerApp.info.packageName : null,intent, resolvedType, resultTo, resultCode, resultData, resultExtras,requiredPermissions, appOp, bOptions, serialized, sticky,callingPid, callingUid, userId);Binder.restoreCallingIdentity(origId);return res;}
}
真正做事情的是ActivityManagerService.broadcastIntentLocked:
int broadcastIntentLocked(ProcessRecord callerApp,String callerPackage, Intent intent, String resolvedType,IIntentReceiver resultTo, int resultCode, String resultData,Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {....// By default broadcasts do not go to stopped apps.intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);// If we have not finished booting, don't allow this to launch new processes.if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);}....// Verify that protected broadcasts are only being sent by system code,// and that system code is only sending protected broadcasts.final String action = intent.getAction();final boolean isProtectedBroadcast;try {isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {Slog.w(TAG, "Remote exception", e);return ActivityManager.BROADCAST_SUCCESS;}final boolean isCallerSystem;switch (UserHandle.getAppId(callingUid)) {case ROOT_UID:case SYSTEM_UID:case PHONE_UID:case BLUETOOTH_UID:case NFC_UID:isCallerSystem = true;break;default:isCallerSystem = (callerApp != null) && callerApp.persistent;break;}//非system app发送protected broadcast,抛出异常if (!isCallerSystem) {if (isProtectedBroadcast) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " from pid="+ callingPid + ", uid=" + callingUid;Slog.w(TAG, msg);throw new SecurityException(msg);} else if (...) {....}}if (action != null) {if (getBackgroundLaunchBroadcasts().contains(action)) {intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);}switch(action) {//处理Package等相关的广播....}}
第一阶段处理一些特殊的广播。
接下来看第二阶段:
// Add to the sticky list if requested.
if (sticky) {//检查是否有发送粘性广播的权限if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,callingPid, callingUid) != PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="+ callingPid + ", uid=" + callingUid+ " requires " + android.Manifest.permission.BROADCAST_STICKY;Slog.w(TAG, msg);throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {Slog.w(TAG, "Can't broadcast sticky intent " + intent+ " and enforce permissions " + Arrays.toString(requiredPermissions));return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}//粘性广播指定发送的组件if (intent.getComponent() != null) {throw new SecurityException("Sticky broadcasts can't target a specific component");}//如果广播不是发送给所有人的,则要查看是否和发送给所有人的广播冲突// We use userId directly here, since the "all" target is maintained// as a separate set of sticky broadcasts.if (userId != UserHandle.USER_ALL) {// But first, if this is not a broadcast to all users, then// make sure it doesn't conflict with an existing broadcast to// all users.ArrayMap<String, ArrayList<Intent>> stickies =mStickyBroadcasts.get(UserHandle.USER_ALL);if (stickies != null) {ArrayList<Intent> list = stickies.get(intent.getAction());if (list != null) {int N = list.size();int i;for (i=0; i<N; i++) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException(+ userId + " conflicts with existing global broadcast");}}}}}ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); if (stickies == null) {stickies = new ArrayMap<>();mStickyBroadcasts.put(userId, stickies);}ArrayList<Intent> list = stickies.get(intent.getAction());if (list == null) {list = new ArrayList<>();stickies.put(intent.getAction(), list);}final int stickiesCount = list.size();int i;for (i = 0; i < stickiesCount; i++) {if (intent.filterEquals(list.get(i))) {// This sticky already exists, replace it.list.set(i, new Intent(intent));break;}}if (i >= stickiesCount) {list.add(new Intent(intent));}
} //end of if(sticky)...// Figure out who all will receive this broadcast.
List receivers = null;//保存匹配该Intent的所有广播接收者,包括静态和动态
List<BroadcastFilter> registeredReceivers = null;//保存符合该Intent的动态receiver
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)== 0) {//如果没有FLAG_RECEIVER_REGISTERED_ONLY flag,则查询PKMS,//获得那些在AndroidManifest.xml中声明的广播接收者,即静态广播接收者,并保存到receiversreceivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) { //没有指定接收的对象if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {// Query one target user at a time, excluding shell-restricted usersfor (int i = 0; i < users.length; i++) {if (mUserController.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {continue;}List<BroadcastFilter> registeredReceiversForUser =mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, users[i]);if (registeredReceivers == null) {registeredReceivers = registeredReceiversForUser;} else if (registeredReceiversForUser != null) {registeredReceivers.addAll(registeredReceiversForUser);}}} else {//从mReceiver查找符合intent的动态广播接收者registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, userId);}}/*判断该广播是否设置了REPLACE_PENDING标签,如果设置了该标签,并且之前的Intent还没有被处理, 则可以使用新的的Intent替换旧的Intent。目的是为了减少不必要的广播发送。*/final boolean replacePending =(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;//先处理动态注册的接收中者int NR = registeredReceivers != null ? registeredReceivers.size() : 0;//此次发送的广播为非有序广播,且符合该intent的动态receiver个数不为0if (!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);}//根据FLAG确定是加入到前台广播队列还是后台广播队列final BroadcastQueue queue = broadcastQueueForIntent(intent);//只需创建一个BrodacstRecord,一个BroadcastRecord对象可包括所有的接收者BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,resultCode, resultData, resultExtras, ordered, sticky, false, userId);final boolean replaced = replacePending&& (queue.replaceParallelBroadcastLocked(r) != null);// Note: We assume resultTo is null for non-ordered broadcasts.if (!replaced) {//如果没有被替换,则保存到mParallelBroadcasts中queue.enqueueParallelBroadcastLocked(r);//调度一次广播发送queue.scheduleBroadcastsLocked();}//至此,动态注册的广播接收者已经处理完毕,设置registeredReceivers为nullregisteredReceivers = null;NR = 0;
}
第二阶段的工作有两项:
- 查询满足条件的动态receiver和静态receiver
- 当本地广播不为ordered时,需尽快发送该广播。另,非ordered的广播都被AMS保存在mParallelBroadcasts中。
// Merge into one list.
int ir = 0;
if (receivers != null) {String skipPackages[] = null;//处理PACKAGE_ADDED、PACKAGE_RESTARTED、PACKAGE_DATA_CLEARED的intent,系统不希望有些应用//一安装就启动。//这类程序的工作原理是:在改程序内部监听PACKAGE_ADDED广播,如果系统没有防备,则PKMS安装完程序 //后所发送的PACKAGE_ADDED消息将触发该应用的启动......//处理ACTION_EXTERNAL_APPLICATIONS_AVAILBLE广播......//将动态注册的接收者registeredReceiver的元素合并到receivers中去//处理完毕后,所有的接收者(无论是动态还是静态)都存储到receivers中了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++;
}...if ((receivers != null && receivers.size() > 0) || resultTo != null) {BroadcastQueue queue = broadcastQueueForIntent(intent);//创建一个BroadcastRecord对象,它的receivers中包括所有的接收者BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,resultData, resultExtras, ordered, sticky, false, userId);final BroadcastRecord oldRecord =replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;if (oldRecord != null) {// Replaced, fire the result-to receiver.if (oldRecord.resultTo != null) {final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);try {oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,oldRecord.intent,Activity.RESULT_CANCELED, null, null,false, false, oldRecord.userId);} catch (RemoteException e) {Slog.w(TAG, "Failure ["+ queue.mQueueName + "] sending broadcast result of "+ intent, e);}}} else {//如果没被替换,将该条广播记录到mOrderedBroadcasts中queue.enqueueOrderedBroadcastLocked(r);//调度AMS进行广播发送工作queue.scheduleBroadcastsLocked();}
} else {// There was nobody interested in the broadcast, but we still want to record// that it happened.if (intent.getComponent() == null && intent.getPackage() == null&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// This was an implicit broadcast... let's record it for posterity.addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);}
}return ActivityManager.BROADCAST_SUCCESS;
} //end of broadcastIntentLocked()
由以上分析可得,AMS将动态receiver和静态receiver都合并到了receivers中了。如果本次广播不是ordered,那么表明动态注册者已经在第二阶段中被处理了。因此在合并时,将不会有动态receiver被加到receivers中。最终所创建的广播记录存在mOrderedBroadcasts中,也即,不管是否为串行发送,静态receiver接收到的广播记录都保存在mOrderedBroadcasts中。
为何不将它们保存到mParallelBroadcasts中呢?
结合code发现,mParallelBroadcasts中的BroadcastRecord所包含的都是动态注册的广播接收者信息,这是因为动态接收者所在的进程是已经存在的,而静态广播接收者就不能保证它已经和一个进程绑定在一起了。如果静态注册的广播接收者所在进程还未启动,则AMS还需要进行创建应用进程,初始化Android运行环境等一系列操作。
且后面,mParallelBroadcasts中的成员是在一个循环中被逐条处理的,如果将静态广播接收者也保存到mParallelBroadcasts中,会有什么后果呢?假设这些静态接收者所在的进程全部未创建和启动,那么AMS将在循环中创建多个进程!系统压力一下就上去了。所以,静态接收者对应的广播记录都被放到了mOrderedBroadcasts中,AMS在处理这类广播信息时,一个进程一个进程地处理,只有处理完一个接收者才继续下一个接收者。这样的好处是避免了惊群效应的出现,坏处是延时较长。
这篇关于【Broadcast】sendBroadcast流程(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!