Android 广播内部机制详解(二)
来源:互联网 发布:mac解压文件 编辑:程序博客网 时间:2024/04/27 10:42
3. 广播的发送
广播的发送,其实也是交给AMS来完成的,首先调用context.sendBroadcast将广播发给AMS的相应函数,AMS再和之前注册的Receiver进行匹配,匹配成功后,就发送给对应的进程。好了,接下来我们通过源码来论证这个结论。
3.1 sendBroadcast
文件:ContextImpl.java
@Override public void sendBroadcast(Intent intent, String receiverPermission) { ...... try { //见3.1.1 ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, null, false, false, getUserId()); } ...... }
很明显,这个是调用了AMS中的broadcastIntent方法
3.1.1 broadcastIntent
文件:ActivityManagerService.java
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是否能够发送 intent = verifyBroadcastLocked(intent); final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); // 见3.2 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; } }
3.2 broadcastIntentLocked
文件:ActivityManagerService.java
这个方法非常的长,我们分成几个部分来讲:
3.2.1 设置广播flag
intent = new Intent(intent); //设置FLAG_EXCLUDE_STOPPED_PACKAGES intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); //没有启动完毕不允许启动新的进程 if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } // 检查发送广播时用户的状态 userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL, "broadcast", callerPackage); if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) { if ((callingUid != Process.SYSTEM_UID || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { Slog.w(TAG, "Skipping broadcast of " + intent + ": user " + userId + " is stopped"); return ActivityManager.BROADCAST_FAILED_USER_STOPPED; } }
主要做了三件事:
所有广播默认会添加这个标志,不会发送广播给stop状态的app。stop状态是指下载后重没有使用过的应用,或者被用户强制停止的应用。不过可以通过让intent携带FLAG_INCLUDE_STOPPED_PACKAGES来framework的设置
没有启动完毕不允许启动新的进程,也就是此时只允许发送动态注册的广播
如果不是 UserHandle.USER_ALL广播,且当前用户不是处于Running状态,这时只能响应系统升级和关机广播
3.2.2 受保护广播的处理
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 (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { if (callerPackage == null) { String msg = "Permission Denial: not allowed to send broadcast " + action + " from unknown caller."; Slog.w(TAG, msg); throw new SecurityException(msg); } else if (intent.getComponent() != null) { if (!intent.getComponent().getPackageName().equals( callerPackage)) { String msg = "Permission Denial: not allowed to send broadcast " + action + " to " + intent.getComponent().getPackageName() + " from " + callerPackage; Slog.w(TAG, msg); throw new SecurityException(msg); } } else { // Limit broadcast to their own package. intent.setPackage(callerPackage); } } }
只允许系统发送手保护的广播(isProtectedBroadcast为true);非系统发送ACTION_APPWIDGET_CONFIGURE或者ACTION_APPWIDGET_UPDATE广播,只能发送给自己本身
3.2.3 处理特定的系统广播
if (action != null) { switch (action) { case Intent.ACTION_UID_REMOVED: case Intent.ACTION_PACKAGE_REMOVED: case Intent.ACTION_PACKAGE_CHANGED: case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: case Intent.ACTION_PACKAGES_SUSPENDED: case Intent.ACTION_PACKAGES_UNSUSPENDED:
处理一些特殊的广播,对于一些package manager的变化,会通知AMS对特定应用进行处理
3.2.4 处理sticky广播
if (sticky) { if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid) != PackageManager.PERMISSION_GRANTED) { // 要在AndroidManifest.xml里面声明android.permission.BROADCAST_STICKY权限 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"); } if (userId != UserHandle.USER_ALL) { // 如果广播不是发送给USER_ALL的,则该广播不能和USER_ALL的广播冲突 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( "Sticky broadcast " + intent + " for user " + userId + " conflicts with existing global broadcast"); } } } } }
这部分主要是说:
- 发送sticky广播需要在AndroidManifest.xml里面声明权限
发送sticky广播不能设置发送权限和具体接收对象
如果不是发送给USER_ALL,则该广播不能和USER_ALL里已有的广播冲突
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))) { // 替换掉已经存在的sticky广播 list.set(i, new Intent(intent)); break; } } if (i >= stickiesCount) { list.add(new Intent(intent)); }
这部分主要是:
通过userId作为key取出mStickyBroadcasts里面保存的所有Sticky广播
如果该sticky广播和mStickyBroadcasts着哦功能保存的一致则替换,不一致则添加到末尾
3.2.5 指明接收用户
int[] users; if (userId == UserHandle.USER_ALL) { // 所有当前已经启动的用户 users = mUserController.getStartedUserArrayLocked(); } else { // 发送给userId这个用户 users = new int[] {userId}; }
3.2.6 指明receivers和registeredReceivers内容
其中receivers存储的是静态注册的接收者;registeredReceivers是动态注册的接收者
List receivers = null; List<BroadcastFilter> registeredReceivers = null; // 没有指定FLAG_RECEIVER_REGISTERED_ONLY标记,允许静态注册 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { // 查询所有满足条件的静态注册的接收者,见3.2.6.1 receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); } if (intent.getComponent() == null) { if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) { for (int i = 0; i < users.length; i++) { // shell用户是否开启允许debug功能 if (mUserController.hasUserRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) { continue; } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false, users[i]); // 查询动态注册的广播 if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { registeredReceivers.addAll(registeredReceiversForUser); } } } else { // 查询动态注册的广播 registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId); } } // 是否可以用新的Intent替换旧的Intent final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; // 动态注册广播的数量 int NR = registeredReceivers != null ? registeredReceivers.size() : 0; // 处理并行广播 if (!ordered && NR > 0) { if (isCallerSystem) { checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, isProtectedBroadcast, registeredReceivers); } // 找到合适的BroadcastQueue ,见3.2.6.2 final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); // queue中等待发送的BroadcastRecord是否有和r一致的 final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); if (!replaced) { // 将r放到BroadcastQueue.mParallelBroadcasts中 queue.enqueueParallelBroadcastLocked(r); // 处理广播 queue.scheduleBroadcastsLocked(); } registeredReceivers = null; NR = 0; }
这部分主要将动态注册的广播接收器和静态注册的广播接收器都找出来,分别放入registeredReceivers和receivers中,然后开始处理非排序的动态广播
3.2.6.1 collectReceiverComponents
文件:ActivityManagerService.java
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, int callingUid, int[] users) { ...... List<ResolveInfo> newReceivers = AppGlobals.getPackageManager() .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList(); ......
这个函数最重要的是getPackageManager,PKMS调用queryIntentReceivers,把AndroidManifest.xml里所有的接收者信息都提取出来
3.2.6.2 broadcastQueueForIntent
文件:ActivityManagerService.java
BroadcastQueue broadcastQueueForIntent(Intent intent) { final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0; return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue; }
如果Intent中的flag设置FLAG_RECEIVER_FOREGROUND则位前台广播,否则为后台广播
3.2.7 处理有序广播
int ir = 0; if (receivers != null) { String skipPackages[] = null; // 不允许自己本身接收自己的ACTION_PACKAGE_ADDED,Intent.ACTION_PACKAGE_RESTARTED和 // Intent.ACTION_PACKAGE_DATA_CLEARED广播 if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction()) || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction()) || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) { Uri data = intent.getData(); if (data != null) { String pkgName = data.getSchemeSpecificPart(); if (pkgName != null) { skipPackages = new String[] { pkgName }; } } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) { skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } // 将静态接收者中将当前被add的应用排除掉 if (skipPackages != null && (skipPackages.length > 0)) { for (String skipPackage : skipPackages) { if (skipPackage != null) { int NT = receivers.size(); for (int it=0; it<NT; it++) { ResolveInfo curt = (ResolveInfo)receivers.get(it); if (curt.activityInfo.packageName.equals(skipPackage)) { receivers.remove(it); it--; NT--; } } } } }
不允许应用接收ACTION_PACKAGE_ADDED,Intent.ACTION_PACKAGE_RESTARTED和Intent.ACTION_PACKAGE_DATA_CLEARED广播,就算是静态注册了也不行,需要在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++; }
将有序的动态注册接收者和静态接收者按照优先级合并到receivers里;合并以后,在receivers链表里,静态接收者对应的是ResolveInfo对象,动态接收者对应的是BroadcastFilter对象
if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); //处理广播,见4.1 queue.scheduleBroadcastsLocked(); } }
如果receivers有接收器,则进行串行广播的发送
- Android 广播内部机制详解(二)
- Android 广播内部机制详解(一)
- Android 广播内部机制详解(三)
- android 广播机制二
- Android 广播机制(二
- Android广播机制二
- Android 广播机制 详解
- Android广播机制详解
- Android广播机制详解
- Android 广播机制 详解
- Android 广播机制 详解
- Android广播机制详解
- Android 广播机制详解
- Android广播机制详解
- Android笔记(二十四)广播机制
- Android中的广播机制(二)----- 发送广播
- 广播机制(二)
- Android Loader 异步加载详解二:探寻Loader内部机制
- RTMP流媒体播放过程
- 强大的vim配置文件,让编程更随意(转)
- RedisLive监控Redis进程状态
- JavaScript中构造函数的注意事项
- Android内存泄漏总结
- Android 广播内部机制详解(二)
- python 除法 /与//在2.7*和3.*版本的区别
- (9) spring boot使用freemarker模板引擎
- 移除导航栏栈数组里的控制器仍然出现的返回箭头
- 减少HTTP请求之将图片转成二进制并生成Base64编码,可以在网页中通过url查看图片(大型网站优化技术)
- solr在java中更新单条数据内容
- TF-IDF与余弦相似性的应用(二):找出相似文章
- 解决mac上sourcetree每次推送需要输入两次密码问题
- jsp:useBean使用详解