相信看到这篇文章的你也一定在为Android8.0及以上的广播机制所困扰吧。经过了曲折的道路,我算是从这个坑里面走出来了,总结一些干货给大家参考,本人能力有限,有错误之处还望指正。
我在Android8.0的模拟器上静态注册开机启动的系统广播接收器时,死活都接不到广播,网上关于这方面的问题感觉总结地不是很完善,但指引我去翻阅了官方文档。看了文档才恍然大悟,原来是8.0开始对广播机制有了限制(如下图)。
其实就是一个大的变动:不能在AndroidManifest.xml(所谓的清单)中注册隐式广播接收器了。系统广播当然都是隐式广播。但是需要签名权限的,以及一些豁免的隐式广播仍可以在清单中注册使用。
看到这句话(黑体那句),我产生了两个疑问:(1)那系统广播现在该如何接收?(2)使用清单注册广播接收器已经没用了吗? 相信这也一定是你所想知道的,那我们依次来看看吧。
(1)静态不行,当然动态注册呀。幸运的是,动态注册还是可以接收系统广播的。方法也很简单,new一个继承于BroadCastReceiver的广播接收器,在为它加上IntentFilter,最后一起注册。以检测系统广播-网络变化为例:
//动态注册
filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
receiver = new MyReceiver();
registerReceiver(receiver,filter);
那么相信你会固执了,如果是我还要实现监听开机广播的app,像这样写成动态注册,开机时这个app还没启动,检测个p啊。对呀,你要是还能这么容易地就检测到,它官方限制个p啊。
不过,我们思考一下,到底我们做错了什么官方要限制我们?试想,我写了100个app,全都要接收开机广播,就像PC有100个开机自启的程序,那么系统开机时多累啊,这样的直接后果就是系统性能严重下降!所以,为了优化系统性能,限制隐式广播是挺有必要的(突然能够理解官方的选择)。
(2)那么清单还能注册广播接收器吗?当然阔以!不过只能是豁免广播的接收器或者接收自己定义的广播的接收器。下面针对自定义广播的接收器进行说明,我在Button的触发事件中发出了一条自己的广播:
sendBC_Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.MY_RECEIVER");
intent.setComponent(new ComponentName("com.example.broadcasttest",
"com.example.broadcasttest.MyReceiver"));
sendBroadcast(intent);
}
});
(注意:一定要设置Component来指明MyReceiver的绝对路径,第一个参数是包名,第二个是全限定类名)
在清单中注册如下:
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.MY_RECEIVER"/>
</intent-filter>
</receiver>
这样,点击button就会显式地(因为指定了路径)发送自定义的标准广播,注册到清单中的MyReceiver也能正确地接收到,并处理一定的事件。
好啦,最后再总结一下全文。回过头想想,其实就是Android8.0开始对广播有了限制:很多隐式广播接收器不能在清单中静态注册。但清单注册广播接收器仍是可以的(豁免广播或自定义广播-注意,自定义广播要显式发送)。实现隐式广播(系统广播)的接收也是可以的,但只能通过动态注册来实现了。
//系统(隐式)广播接收器:动态注册 (豁免的可静态注册)
//自定义广播接收器:动态注册 or 静态注册(此时需要显式发送)
最最后的建议,方便的话尽量都用动态注册吧,感谢阅读。