[TOC]

概述

项目中遇到下面这样一个问题:

在Android8.0(target?=26)上面,App发送的隐式广播无法被接收到。

1
08-23 21:45:38.271  1207  1225 W BroadcastQueue: Background execution not allowed: receiving Intent { act=com.xxx.xxx flg=0x10 (has extras) } to com.xx.xx/com.xx.xx.receiver.XxReceiver

原因分析:

​ 基于以上种种原因,从Android O开始,Google做了一系列规避措施,最直接的体现在两个方面:

​ 取消静态广播的支持。
​ 以前很好用的静态广播消息将接收不到,比如开机广播:“android.intent.action.BOOT_COMPLETED”,

​ 网络状态发生改变广播:“android.net.conn.CONNECTIVITY_CHANGE”。

​ 必须以动态的方式才能接收到,这就保证了”只有用户将你的应用置为前台应用,也就是用户正在使用你的应用的时候,该应用才有可能收到广播“。这一举措旨在解决应用开机自启动泛滥的问题。

​ 更改Service的启动方式。
​ 如果一个应用在后台去startService(比如在broadcast中),企图在后台悄悄的运行跑流量,这将直接抛出异常。和广播类似,要启动一个服务,必须是”用户正在使用你的应用期间“才可行,或者你的服务是一个前台服务。这一举措旨在解决”应用后台自启动,成为不死应用,占用大量资源“的问题。

开机广播

​ 对于android.intent.action.BOOT_COMPLETED 这个广播,虽然被禁用了,但是在实际的开发中还是很有必要的存在,很多操作还是需要该广播的支持。所以Google也没有把这个广播完全封死,如果应用的android:sharedUserId=“android.uid.system”,那么该应用还是可以以静态广播的方式注册和监听到它。而对于第三方应用,显然已经无法再收到这条广播了。

网络状态改变广播

​ 对于android.net.conn.CONNECTIVITY_CHANGE,显然就没这么友好了,即使是android:sharedUserId=“android.uid.system”的应用,也将收不到静态注册的广播消息。如要接收该消息,只能是两种方式:

​ 以动态注册广播形式注册

​ 以静态注册广播形式注册,在AndroidManifest.xml中指明该应用的SDK为低于26,比如 < uses-sdk android:minSdkVersion=“23” android:targetSdkVersion=“23” />

影响

首先要了解一个概念。官方文档里提到implicit broadcast,可译为隐式广播,指那些没有指定接收App(即包名)的广播。

系统发送的广播毫无疑问都是隐式广播,因此基本上都会受到影响,除了部分受豁免广播之外

App发送的自定义隐式广播,都会受到影响。

如何应对这一限制

分析了受限制的原因之后,就知道该如何应对这一影响了。

  • 优先使用动态注册Receiver的方式,能动态注册绝不使用Manifest注册
1
2
3
4
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.frewen.android.demo.action");
TheReceiver receiver = new TheReceiver();
registerReceiver(receiver, intentFilter);

如果一定要Manifest注册,那么当发送广播的时候,指定广播接收者的包名,即发送显式广播

1
2
3
4
5
Intent intent = new Intent("com.frewen.android.demo.action");
intent.putExtra("receive","test broadcast");
intent.setPackage(getPackageName());
//intent.setComponent(...)
sendBroadcast(intent);

如果要接收系统广播,而对应的广播在Android8.0中无法被接收,那么只能暂时把App的targetSdkVersion改为25或以下。