Android O对隐式广播进行了限制, 其限制链接说明: https://developer.android.com/about/versions/oreo/background
如果应用注册为接收广播,则在每次发送广播时,应用的接收器都会消耗资源。 如果多个应用注册为接收基于系统事件的广播,这会引发问题;触发广播的系统事件会导致所有应用快速地连续消耗资源,从而降低用户体验。
为了缓解这一问题,Android 7.0(API 级别 25)对广播施加了一些限制,如后台优化中所述。
Android 8.0 让这些限制更为严格。
针对 Android 8.0 的应用无法继续在其清单中为隐式广播注册广播接收器。 隐式广播是一种不专门针对该应用的广播。 例如,ACTION_PACKAGE_REPLACED 就是一种隐式广播,因为它将发送到注册的所有侦听器,让后者知道设备上的某些软件包已被替换。
不过,ACTION_MY_PACKAGE_REPLACED 不是隐式广播,因为不管已为该广播注册侦听器的其他应用有多少,它都会只发送到软件包已被替换的应用。
应用可以继续在它们的清单中注册显式广播。
应用可以在运行时使用 Context.registerReceiver() 为任意广播(不管是隐式还是显式)注册接收器。
需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用。
在许多情况下,之前注册隐式广播的应用使用 JobScheduler 作业可以获得类似的功能。
例如,一款社交照片应用可能需要不时地执行数据清理,并且倾向于在设备连接到充电器时执行此操作。
之前,应用已经在清单中为 ACTION_POWER_CONNECTED 注册了一个接收器;当应用接收到该广播时,它会检查清理是否必要。 为了迁移到 Android 8.0,应用将该接收器从其清单中移除。
应用将清理作业安排在设备处于空闲状态和充电时运行。
注:很多隐式广播当前均已不受此限制所限。 应用可以继续在其清单中为这些广播注册接收器,不管应用针对哪个 API 级别。 有关已豁免广播的列表,请参阅隐式广播例外。
如何应对限制
1、将静态注册修改为动态注册
2、通过setPackage或者setComponent将隐式广播修改为显示广播,不过这种方式的话,只能发送给一个应用
3、04-21 04:12:27.513 2431 4821 W BroadcastQueue: Background execution not allowed:******************
这个log是在BroadcastQueue.java#1275中
1267 } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
1268 || (r.intent.getComponent() == null
1269 && r.intent.getPackage() == null
1270 && ((r.intent.getFlags()
1271 & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
1272 && !isSignaturePerm(r.requiredPermissions))) {
1273 mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
1274 component.getPackageName());
1275 Slog.w(TAG, "Background execution not allowed: receiving "
1276 + r.intent + " to "
1277 + component.flattenToShortString());
1278 skip = true;
1279 }
r.intent.getFlags() & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0 不能带有FLAG_RECEIVER_INCLUDE_BACKGROUND这个标志位,这就是我们实现的关键点
FLAG_RECEIVER_INCLUDE_BACKGROUND这个标志位在AS中是找不到的。
/**
* If set, the broadcast will always go to manifest receivers in background (cached
* or not running) apps, regardless of whether that would be done by default. By
* default they will only receive broadcasts if the broadcast has specified an
* explicit component or package name.
*
* NOTE: dumpstate uses this flag numerically, so when its value is changed
* the broadcast code there must also be changed to match.
*
* @hide
*/
public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
发送广播的时候携带intent.addFlags(0x01000000); 即能让广播突破隐式广播限制。但是这种方式AS中会提醒有错误,可以编译通过。
4、通过queryBroadcastReceivers检索接收intent的接收者
queryBroadcastReceivers是PackageManager中的一个检索所有Intent的接收者的方法,返回一个ResolveInfo的集合。
private static void sendImplicitBroadcast(Context ctxt, Intent i) {
PackageManager pm=ctxt.getPackageManager();
List<ResolveInfo> matches=pm.queryBroadcastReceivers(i, 0);
for (ResolveInfo resolveInfo : matches) {
Intent explicit=new Intent(i);
ComponentName cn=
new ComponentName(resolveInfo.activityInfo.applicationInfo.packageName,
resolveInfo.activityInfo.name);
explicit.setComponent(cn);
ctxt.sendBroadcast(explicit);
}
}