背景
- 最近编写代码,在使用广播的时候遇到了些问题,发现有的广播按原来的使用方式,竟然不灵了...主要是关于一些静态广播的注册问题。以前写APP总是会在
AndroidManifest
中静态监听很多系统广播,希望能尽量延长APP的存活时间,提升自启能力,当然这只是其中的一种方法。很多APP都是这么干的。所以Google加强了对这块的优化,防止应用频繁拉起,降低功耗。
如何确定一个action是否不支持静态注册?
- 静态注册系统广播时,在检查代码没有问题的情况下,基本上都可以确定是系统禁止了。而不同的Android大版本对这些action的禁止策略又是不相同的。像
ConnectivityManager.CONNECTIVITY_ACTION
就是从Android N开始才不支持静态注册的,当然还有其他的。那么如何确定一个action
是否不支持静态注册了呢?
我大概整理的一些:
ConnectivityManager.CONNECTIVITY_ACTION
Android 7.0 (API level 24)及以上开始,必须要动态注册
应用无法发送或接收ACTION_NEW_PICTURE
或ACTION_NEW_VIDEO
此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。
frameworks\base\core\java\android\content\Intent.java
里注释的几个 明确不允许 静态注册的广播
/**
* You <em>cannot</em> receive this through components declared in
* manifests, only by explicitly registering for it with
* {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver()}.
*/
public static final String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
public static final String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
其他的 Action 不确定的可以在源码下查找发送该 Action 对应广播的地方,看 Intent 有没有带上Intent.FLAG_RECEIVER_REGISTERED_ONLY
这个flag,也就是声明了这个 Action 只允许动态注册的 receiver 接收。
/**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000;
Android O(8.0)上对隐式广播的限制
- 在Android O上,从应用A向应用B发送广播,以前都是这么写的:
Intent intent = new Intent();
intent.setAction("com.broadcast.test.action");
sendBroadcast(intent);
而B中只需要注册一个 监听了 "com.broadcast.test.action"
的广播即可,无论静态注册还是动态注册。
而在Android O的机器上,这样操作B无论如何都是收不到消息的。
07-27 15:58:57.653: W/BroadcastQueue(4268): Background execution not allowed: receiving Intent { act=com.broadcast.test.action flg=0x4000010 (has extras) } to com.broadcast.test.demo/.MyReceiver
当然这也同时取决于 B 应用的 targetVersion , 经过试验,只有当 B 的 targetVersion 是 26 (Android 8.0)时才会有问题。所以这种情况是出现在 targetVersion 在26及以上的APP 运行在 Android 8.0及以上版本的机器上时,在接收隐式intent时会出现问题
- 怎么办呢?
- 接收方APP
targetVersion
改成低于 26 的,就直接没有这个问题了 - 系统需要明确知道你到底是想发送给谁?用以下几种方式都可以,我都实操过
Intent intent = new Intent();
方法1:
intent.setAction("com.broadcast.test.action");
intent.setPackage("com.broadcast.test.demo");
方法2:
intent.setClassName("com.broadcast.test.demo", "com.broadcast.test.demo.MyReceiver");
方法3:
intent.setComponent(new ComponentName("com.broadcast.test.demo", "com.broadcast.test.demo.MyReceiver"));
getApplicationContext().sendBroadcast(intent);
,