一、概述
广播(Broadcast)机制用于进程/线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者BroadcastReceiver便是Android四大组件之一。
BroadcastReceiver分为两类:
- 静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver。
- 动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver,动态注册更为灵活,可在不需要时通过unregisterReceiver()取消注册。
从广播发送方式可分为三类:
- 普通广播:通过Context.sendBroadcast()发送,可并行处理
- 有序广播:通过Context.sendOrderedBroadcast()发送,串行处理
- Sticky广播:通过Context.sendStickyBroadcast()发送
二、注册广播
广播注册,对于应用开发来说,往往是在Activity/Service中调用registerReceiver()
方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。另外调用getOuterContext()可获取最外层的调用者Activity或Service。
2.1 registerReceiver
[ContextImpl.java]
2.2 registerReceiverInternal
[ContextImpl.java]
2.3 getReceiverDispatcher
LoadedApk.java
LoadedApk是用来保存当前运行app的状态类它保存着app的application、类加载器、receiver、service等信息。
管理者mReceivers:ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();可以看出BroadcastReceiver与ReceiverDispatcher一一对应关系。
管理着当前进程所有已注册的广播接收者BroadcastReceiver,ReceiverDispatcher封装一个广播接收者及IIntentReceiver(ReceiverDispatcher的内部类,并且继承了IIntentReceiver.Stub所以理解为其远程服务对象),以此通过远程服务对象访问封装的广播接收者。
2.3.1 创建ReceiverDispatcher
LoadedApk.java
2.3.2 创建InnerReceiver
2.4 AMP.registerReceiver
略直接进入AMS
2.5 AMS.registerReceiver
其中mRegisteredReceivers
记录着所有已注册的广播,以receiver IBinder为key, ReceiverList为value为HashMap。
在BroadcastQueue中有两个广播队列mParallelBroadcasts,mOrderedBroadcasts,数据类型都为ArrayList:
mParallelBroadcasts
:并行广播队列,可以立刻执行,而无需等待另一个广播运行完成,该队列只允许动态已注册的广播,从而避免发生同时拉起大量进程来执行广播,前台的和后台的广播分别位于独立的队列。mOrderedBroadcasts
:有序广播队列,同一时间只允许执行一个广播,该队列顶部的广播便是活动广播,其他广播必须等待该广播结束才能运行,也是独立区别前台的和后台的广播。
2.5.1 ReceiverList()
ReceiverList.java
后面广播处理时就会从ReceiverList中找到receiver的Binder实例,并且此对象也会保存到对应进程的ProcessRecord对象中以及AMS的mRegisteredReceivers(已创建的广播接收者队列)中
2.5.2 BroadcastFilter()
BroadcastFilter.java
从注册时2.5代码可以看出每个广播接收者对应着一个ReceiverList,一个ReceiverList存放着BroadcastFilter对象
后面广播处理时主要是通过BroadcastFilter匹配,匹配成功则获取到BroadcastFilter.ReceiverList,然后通过ReceiverList.receiver找到广播接收者,然后进行处理
2.5.3 AMS.broadcastQueueForIntent
broadcastQueueForIntent(Intent intent)通过判断intent.getFlags()是否包含FLAG_RECEIVER_FOREGROUND 来决定是前台或后台广播,进而返回相应的广播队列mFgBroadcastQueue或者mBgBroadcastQueue。
- 当Intent的flags包含FLAG_RECEIVER_FOREGROUND,则返回mFgBroadcastQueue;
- 当Intent的flags不包含FLAG_RECEIVER_FOREGROUND,则返回mBgBroadcastQueue;
2.6 广播注册小结
注册广播:
- 传递的参数为广播接收者BroadcastReceiver和Intent过滤条件IntentFilter;
- 创建对象LoadedApk.ReceiverDispatcher.InnerReceiver,该对象继承于IIntentReceiver.Stub;
- 通过AMS把当前进程的ApplicationThread和InnerReceiver对象的代理类,注册登记到system_server进程;
- 当广播receiver没有注册过,则创建广播接收者队列ReceiverList,该对象继承于ArrayList, 并添加到AMS.mRegisteredReceivers(已注册广播队列);
- 创建BroadcastFilter,并添加到AMS.mReceiverResolver;
- 将BroadcastFilter添加到该广播接收者的ReceiverList
另外,当注册的是Sticky广播:
- 创建BroadcastRecord,并添加到BroadcastQueue的mParallelBroadcasts(并行广播队列),注册后调用AMS来尽快处理该广播。
- 根据注册广播的Intent是否包含FLAG_RECEIVER_FOREGROUND,则mFgBroadcastQueue
广播注册完, 另一个操作便是在广播发送过程.
三、 发送广播
发送广播是在Activity或Service中调用sendBroadcast()
方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。
3.1 sendBroadcast
ContextImpl.java
3.2 AMS.broadcastIntent
broadcastIntent()方法有两个布尔参数serialized和sticky来共同决定是普通广播,有序广播,还是Sticky广播,参数如下:
类型 | serialized | sticky |
---|---|---|
sendBroadcast | false | false |
sendOrderedBroadcast | true | false |
sendStickyBroadcast | false | true |
3.3 AMS.broadcastIntentLocked
/*
概述
1、设置flag
2、广播权限验证
3、处理系统相关广播
4、增加sticky广播
5、查询receivers和registeredReceivers
6、处理并行广播
7、合并registeredReceivers到receivers
8、处理串行广播
*/
private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission, int appOp,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
//1、增加flag,则广播不会发送给已停止的package(保证已停止的app不会收到该广播)
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
if (DEBUG_BROADCAST_LIGHT) Slog.v(
TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
+ " ordered=" + ordered + " userid=" + userId);
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
userId = handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_NON_FULL, "broadcast", callerPackage);
// Make sure that the user who is receiving this broadcast is running.
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
//检查发送广播时用户状态(当非USER_ALL广播且当前用户并没有处于Running的情况下,
//除非是系统升级广播或者关机广播,否则直接返回)
if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
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;
}
}
这个过程最重要的工作是:
- 添加flag=
FLAG_EXCLUDE_STOPPED_PACKAGES
,保证已停止app不会收到该广播; - 当系统还没有启动完成,则不允许启动新进程,,即只有动态注册receiver才能接受广播
- 当非USER_ALL广播且当前用户并没有处于Running的情况下,除非是系统升级广播或者关机广播,否则直接返回。
BroadcastReceiver还有其他flag,位于Intent.java常量:
FLAG_RECEIVER_REGISTERED_ONLY //只允许已注册receiver接收广播
FLAG_RECEIVER_REPLACE_PENDING //新广播会替代相同广播
FLAG_RECEIVER_FOREGROUND //只允许前台receiver接收广播
FLAG_RECEIVER_NO_ABORT //对于有序广播,先接收到的receiver无权抛弃广播
FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT //Boot完成之前,只允许已注册receiver接收广播
FLAG_RECEIVER_BOOT_UPGRADE //升级模式下,允许系统准备就绪前可以发送广播
/*
* Prevent non-system code (defined here to be non-persistent
* processes) from sending protected broadcasts.
*/
//2、广播权限验证
int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
|| callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
|| callingAppId == Process.NFC_UID || callingUid == 0) {
// Always okay.
//当调用者进程为null或者非persistent(常驻进程有android:sharedUserId="android.uid.system"和android:persistent="true"两个属性修饰,这就意味着,persistent应用一定只能是系统应用)进程的情况下
} else if (callerApp == null || !callerApp.persistent) {
try {
//当发送的是受保护的广播mProtectedBroadcasts(只允许系统使用),则抛出异常
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);
//当action为ACTION_APPWIDGET_CONFIGURE时,虽然不希望应用发送这种广播,处于兼容性考虑,限制该广播只允许发送给自己
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
// Special case for compatibility: we don't want apps to send this,
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
// just limit it to the caller.
if (callerApp == null) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from unknown caller.";
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (intent.getComponent() != null) {
// They are good enough to send to an explicit component... verify
// it is being sent to the calling app.
if (!intent.getComponent().getPackageName().equals(
callerApp.info.packageName)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " to "
+ intent.getComponent().getPackageName() + " from "
+ callerApp.info.packageName;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
intent.setPackage(callerApp.info.packageName);
}
}
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
}
主要功能:
- 对于callingAppId为SYSTEM_UID,PHONE_UID,SHELL_UID,BLUETOOTH_UID,NFC_UID之一或者callingUid == 0时都畅通无阻;
- 否则当调用者进程为空 或者非persistent进程的情况下:
- 当发送的是受保护广播
mProtectedBroadcasts
(只允许系统使用),则抛出异常; - 当action为ACTION_APPWIDGET_CONFIGURE时,虽然不希望该应用发送这种广播,处于兼容性考虑,限制该广播只允许发送给自己,否则抛出异常。
- 当发送的是受保护广播
//3、处理系统相关广播
final String action = intent.getAction();
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED://uid移除
case Intent.ACTION_PACKAGE_REMOVED://package移除
case Intent.ACTION_PACKAGE_CHANGED://package改变
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE://外部设备不可用
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE://外部设备可用
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
if (checkComponentPermission(
android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
callingPid, callingUid, -1, true)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: " + intent.getAction()
+ " broadcast from " + callerPackage + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires "
+ android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
switch (action) {
case Intent.ACTION_UID_REMOVED://uid移除
final Bundle intentExtras = intent.getExtras();
final int uid = intentExtras != null
? intentExtras.getInt(Intent.EXTRA_UID) : -1;
if (uid >= 0) {
BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics();
synchronized (bs) {
bs.removeUidStatsLocked(uid);
}
mAppOpsService.uidRemoved(uid);
}
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE://外部设备不可用
// If resources are unavailable just force stop all those packages
// and flush the attribute cache as well.
String list[] =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (list != null && list.length > 0) {
for (int i = 0; i < list.length; i++) {
forceStopPackageLocked(list[i], -1, false, true, true,
false, false, userId, "storage unmount");
}
cleanupRecentTasksLocked(UserHandle.USER_ALL);
sendPackageBroadcastLocked(
IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list,
userId);
}
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE://外部设备可用
cleanupRecentTasksLocked(UserHandle.USER_ALL);
break;
case Intent.ACTION_PACKAGE_REMOVED://package移除
case Intent.ACTION_PACKAGE_CHANGED://package改变
Uri data = intent.getData();
String ssp;
if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
boolean fullUninstall = removed &&
!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
forceStopPackageLocked(ssp, UserHandle.getAppId(
intent.getIntExtra(Intent.EXTRA_UID, -1)),
false, true, true, false, fullUninstall, userId,
removed ? "pkg removed" : "pkg changed");
}
if (removed) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
new String[] {ssp}, userId);
if (fullUninstall) {
mAppOpsService.packageRemoved(
intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
// Remove all permissions granted from/to this package
removeUriPermissionsForPackageLocked(ssp, userId, true);
removeTasksByPackageNameLocked(ssp, userId);
if (userId == UserHandle.USER_OWNER) {
mTaskPersister.removeFromPackageCache(ssp);
}
}
} else {
removeTasksByRemovedPackageComponentsLocked(ssp, userId);
if (userId == UserHandle.USER_OWNER) {
mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
}
}
}
break;
}
break;
case Intent.ACTION_PACKAGE_ADDED://增加package
// Special case for adding a package: by default turn on compatibility mode.
Uri data = intent.getData();
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
final boolean replacing =
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
mCompatModePackages.handlePackageAddedLocked(ssp, replacing);
if (replacing) {
removeTasksByRemovedPackageComponentsLocked(ssp, userId);
}
if (userId == UserHandle.USER_OWNER) {
mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
}
}
break;
case Intent.ACTION_TIMEZONE_CHANGED://时区改变,通知所有运行中的进程
// If this is the time zone changed action, queue up a message that will reset
// the timezone of all currently running processes. This message will get
// queued up before the broadcast happens.
mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
break;
case Intent.ACTION_TIME_CHANGED://时间改变,通知所有运行中的进程
// If the user set the time, let all running processes know.
final int is24Hour =
intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1
: 0;
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteCurrentTimeChangedLocked();
}
break;
case Intent.ACTION_CLEAR_DNS_CACHE://DNS缓存清空
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
break;
case Proxy.PROXY_CHANGE_ACTION://网络代理改变
ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
break;
}
}
// Add to the sticky list if requested.
//4、sticky广播不做分析
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 (requiredPermission != null) {
Slog.w(TAG, "Can't broadcast sticky intent " + intent
+ " and enforce permission " + requiredPermission);
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(
"Sticky broadcast " + intent + " for user "
+ userId + " conflicts with existing global broadcast");
}
}
}
}
}
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<String, ArrayList<Intent>>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<Intent>();
stickies.put(intent.getAction(), list);
}
int N = list.size();
int i;
for (i=0; i<N; i++) {
if (intent.filterEquals(list.get(i))) {
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
if (i >= N) {
list.add(new Intent(intent));
}
}
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
users = mStartedUserArray;
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
}
// Figure out who all will receive this broadcast.
//5、查询receivers和registeredReceivers
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
//当允许静态接收者处理该广播,则通过PKMS根据Intent查询相应静态receivers
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
//记录着匹配当前intent的所有静态注册广播接收者
//(通过PKMS,获取从AndroidMainfest.xml中声明并解析出来的信息保存在PKMS.mReceivers中)
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
//根据userId来决定是发送给全部的接收者,还是指定的userId
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
UserManagerService ums = getUserManagerLocked();
for (int i = 0; i < users.length; i++) {
if (ums.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 {
//查询相应的动态注册的广播接收者(匹配当前intent的所有动态广播接收者)
//主要通过传入参数进行匹配(filter.match())找到registeredReceivers
//filter.match()匹配过程暂时忽略
//广播注册时有讲到mReceiverResolver,注册时将BroadcastFilter加入列表
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}
//用于标识是否需要用新intent替换旧的intent。
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (DEBUG_BROADCAST) Slog.v(TAG, "Enqueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);
//6、处理并行广播
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.
final BroadcastQueue queue = broadcastQueueForIntent(intent);
//创建BroadcastRecord对象
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
appOp, registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
//将BroadcastRecord加入到并行广播队列
queue.enqueueParallelBroadcastLocked(r);
//处理广播
【4.1】
queue.scheduleBroadcastsLocked();
}
//动态注册的广播接收者处理完成,则会置空改变量
registeredReceivers = null;
NR = 0;
}
主要工作并行广播处理
1、首先创建BroadcastRecord广播对象,注意重要的一个参数registeredReceivers(处理该广播的所有BroadcastFilter,通过BroadcastFilter来找到对应的广播接收者)
2、将广播加入到并行广播队列
3、广播处理【4.1】后面介绍
// Merge into one list.
//7、串行广播receivers确认
//有序广播处理或者普通广播允许静态注册广播处理时的receivers确认(其标志:Intent.FLAG_RECEIVER_REGISTERED_ONLY)
//直白点就是串行广播处理
int ir = 0;
if (receivers != null) {
// A special case for PACKAGE_ADDED: do not allow the package
// being added to see this broadcast. This prevents them from
// using this as a back door to get run as soon as they are
// installed. Maybe in the future we want to have a special install
// broadcast or such for apps, but we'd like to deliberately make
// this decision.
//防止应用监听该广播,在安装时直接运行
String skipPackages[] = null;
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);
}
//将skipPackages相关广播接收者从receivers列表中移除
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--;
}
}
}
}
}
//有一个处理动态广播的过程,处理完后再执行将动态注册的registeredReceivers合并到receivers
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
//如果NR == 0有可能是发送的普通广播下允许静态广播(并行广播处理完清0)
//如果NR!=0则为有序广播,将registeredReceivers合并到receivers中
//最终就是确认receivers进行串行处理
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++;
}
//8、处理串行广播
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
//创建BroadcastRecord,非常重要的参数registeredReceivers(处理该广播的所有BroadcastFilter,通过BroadcastFilter来找到对应的广播接收者)
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) {
int seq = r.intent.getIntExtra("seq", -1);
Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
}
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
//将BroadcastRecord加入到有序广播队列
queue.enqueueOrderedBroadcastLocked(r);
//处理广播
queue.scheduleBroadcastsLocked();
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
3.4 小结
发送广播过程:
- 默认不发送给已停止(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES)的应用包;
- 处理各种PACKAGE,TIMEZONE等相关的系统广播;
- 当为粘性广播,则将sticky广播增加到list,并放入mStickyBroadcasts里面;
- 当广播的Intent没有设置FLAG_RECEIVER_REGISTERED_ONLY,则允许静态广播接收者来处理该广播; 创建BroadcastRecord对象,并将该对象加入到相应的广播队列, 然后调用BroadcastQueue的
scheduleBroadcastsLocked
()方法来完成的不同广播处理:
处理方式:
- Sticky广播: 广播注册过程处理AMS.registerReceiver,开始处理粘性广播;
- 创建BroadcastRecord对象;
- 并添加到mParallelBroadcasts队列;
- 然后执行queue.scheduleBroadcastsLocked;
- 并行广播: 广播发送过程处理
- 只有动态注册的mRegisteredReceivers才会并行处理;
- 会创建BroadcastRecord对象;
- 并添加到mParallelBroadcasts队列;
- 然后执行queue.scheduleBroadcastsLocked;
- 串行广播: 广播发送广播处理
- 所有静态注册的receivers以及动态注册mRegisteredReceivers合并到一张表处理;
- 创建BroadcastRecord对象;
- 并添加到mOrderedBroadcasts队列;
- 然后执行queue.scheduleBroadcastsLocked;
可见不管哪种广播方式,接下来都会执行scheduleBroadcastsLocked方法来处理广播;
四、 处理广播
在发送广播过程中会执行scheduleBroadcastsLocked
方法来处理相关的广播
4.1 scheduleBroadcastsLocked()
BroadcastQueue.java
在BroadcastQueue对象创建时,mHandler=new BroadcastHandler(handler.getLooper());那么此处交由mHandler的handleMessage来处理:
AMS初始化创建BroadcastQueue时将mHandlerThread前台线程传递给BroadcastQueue.mHandler
接着流程mHandler消息处理
引爆超时炸弹后面广播处理时会发送一个延时炸弹消息,当广播在规定时间处理完成移除炸弹否则炸弹引爆,引爆后进行超时处理
4.2 processNextBroadcast
BroadcastQueue.java
主要功能
while循环处理并行广播【5.1】为动态注册广播方式处理,并加入历史统计
主要功能
待处理广播的进程的一个状态判断,是否等待或者处理下一条广播
主要功能
do{}while()是对当前的有序广播的一个处理,并非处理广播接收器,主要做了一些判断
1、首先从广播队列中获取一个广播,接着判断此广播是否超时,超时处理
2、判断此广播是否发送完成,完成后是否需要结束广播接收者处理(发送广播时会设置一个广播处理完成的一个广播接收器回调)用于通知广播处理完成,处理流程【5.1.1】
3、 r = mOrderedBroadcasts.get(0);获取到广播之后,do{}while()结束后对r广播进行广播接收器处理
主要功能是对接收方权限的验证等等,最终ok后确认是否启动进程
主要功能不需要启动接收者进程,执行【5.2】静态注册广播方式处理
主要功能接收者进程未启动,启动进程,无法启动时结束当前接收者处理,启动成功将当前接收者处理的广播设置成待处理广播
进程启动后会执行【5.3】AMS.attachApplicationLocked(),继续广播处理,后面介绍
4.2.1 deliverToRegisteredReceiverLocked()
BroadcastQueue.java
主要功能
1、检查发送者是否有BroadcastFilter所需权限
2、以及接收者是否有发送者所需权限
3、当权限不满足要求,则skip=true
主要功能串行广播处理,进入【5.1.1】performReceiverLocked()
5.1.1 performReceiveLocked()
BroadcastQueue.java
主要功能确认广播处理路径
当广播发送进程存在时,通过发送进程来进行调用接收器广播接收处理 ,否则直接在系统进程调用接收器广播接收处理
5.1.2 scheduleRegisteredReceiver()
ActivityThread.java
不管是发送广播进程还是系统进程,最终都是执行广播接收器服务的receiver.performReceive【5.1.3】
5.1.3 performReceive()
LoadedApk.java
接着分析【5.1.4】
5.1.4 ReceiverDispatcher.performReceive
.LoadedApk.java
主要功能将广播处理发送给Handler处理,这个handler是在广播接收器注册时确定的
继续分析【5.1.5】
5.1.5 LoadedApk.ReceiverDispatcher.Args
以上任务主要是执行广播接收器的onReceiver方法,以及执行完【5.1.7】finish处理
继续分析
5.1.6 onReceiver
BroadcastReceiver.java
抽象方法,要看开发人员如何实现了
继续分析
5.1.7 BroadcastReceiver.PendingResult.finish()
BroadcastReceiver.java
继续分析5.1.8
5.1.8 BroadcastReceiver.PendingResult.sendFinished()
BroadcastReceiver.java
主要功能广播接收器处理完,通知AMS处理完成
继续分析
5.1.9 AMS.finishReceiver()
AMS接收到广播处理完通知,首先清理当前广播接收器广播状态,然后处理下一条广播【4.2】过程,前面有讲
接下来回到【4.2】中继续分析【5.2】
5.2 processCurBroadcastLocked()
BroadcastQueue.java
主要功能确认接收者进程状态,并陷入到接收者进程(由于此广播接收器未注册,并不存在广播接收器远程服务对象)
所以由接收者进程主线程接收处理,来进行后续的广播接收器处理。
继续分析【5.2.1】
5.2.1 AT.scheduleReceiver
交给handler线程处理
5.2.2 AT.handlerReceiver
主要功能,由于此接收器未注册所以不存在接收器远程binder服务对象,首先反射创建广播接收器,然后执行接收器的onReceiver方法
处理完成后要通知AMS处理完成流程【5.1.7】已经介绍过,不再赘述。
接着【4.2】processNextBroadcast方法中继续处理【5.3】进程启动(不在此赘述)后的处理流程
5.3 AMS.attachApplicationLocked()
进程创建后,紧接着广播继续处理【5.3.1】
5.3.1 AMS.sendPendingBroadcastsLocked
交给BroadcastQueue继续处理
5.3.2 sendPendingBroadcastsLocked()
BroadcastQueue.java
回看【5.2】流程,此处不再介绍
至此广播处理流程完成。
五、总结
5.1 基础知识
1.BroadcastReceiver分为两类:
- 静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver;
- 动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver, 不需要时记得调用unregisterReceiver();
2.广播发送方式可分为三类:
类型 | 方法 | ordered | sticky |
---|---|---|---|
普通广播 | sendBroadcast | false | false |
有序广播 | sendOrderedBroadcast | true | false |
Sticky广播 | sendStickyBroadcast | false | true |
3.广播注册registerReceiver():默认将当前进程的主线程设置为scheuler. 再向AMS注册该广播相应信息, 根据类型选择加入mParallelBroadcasts或mOrderedBroadcasts队列.
4.广播发送processNextBroadcast():根据不同情况调用不同的处理过程:
- 如果是动态广播接收者,则调用deliverToRegisteredReceiverLocked处理;
- 如果是静态广播接收者,且对应进程已经创建,则调用processCurBroadcastLocked处理;
- 如果是静态广播接收者,且对应进程尚未创建,则调用startProcessLocked创建进程。
5.2 广播处理机制
- 当发送串行广播(ordered=true)的情况下:
- 静态注册的广播接收者(receivers),采用串行处理;
- 动态注册的广播接收者(registeredReceivers),采用串行处理;
- 当发送并行广播(ordered=false)的情况下:
- 静态注册的广播接收者(receivers),依然采用串行处理;
- 动态注册的广播接收者(registeredReceivers),采用并行处理;
简单来说,静态注册的receivers始终采用串行方式来处理(processNextBroadcast); 动态注册的registeredReceivers处理方式是串行还是并行方式, 取决于广播的发送方式(processNextBroadcast)。
静态注册的广播往往其所在进程还没有创建,而进程创建相对比较耗费系统资源的操作,所以 让静态注册的广播串行化,能防止出现瞬间启动大量进程的喷井效应。
ANR时机:只有串行广播才需要考虑超时,因为接收者是串行处理的,前一个receiver处理慢,会影响后一个receiver;并行广播 通过一个循环一次性向所有的receiver分发广播事件,所以不存在彼此影响的问题,则没有广播超时;
- 串行广播超时情况1:某个广播总处理时间 > 2* receiver总个数 * mTimeoutPeriod, 其中mTimeoutPeriod,前台队列默认为10s,后台队列默认为60s;
- 串行广播超时情况2:某个receiver的执行时间超过mTimeoutPeriod;