目录
注册方式
静态注册(Manifest-declared receivers)
在Manifest中进行申明,系统会在广播发出后启动您的应用(如果应用尚未运行)。
注意:如果您的应用以 API 级别 26 或更高级别的平台版本为目标,则不能使用清单为隐式广播(没有明确针对您的应用的广播)声明接收器,但一些不受此限制的隐式广播除外。在大多数情况下,您可以使用调度作业来代替。
<!-- If this receiver listens for broadcasts sent from the system or from
other apps, even other apps that you own, set android:exported to "true". -->
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="APP_SPECIFIC_BROADCAST" />
</intent-filter>
</receiver>
动态注册(Context-registered receivers)
val br: BroadcastReceiver = MyBroadcastReceiver()
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
// 对应android:exported = false
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
生命周期
广播的生命周期分为根据注册方式分为两种
静态注册的生命周期非常简单且短暂,当系统发出广播后会调用onReceive
,当onReceive
执行完毕后就会回收实例。
动态注册的生命周期以registerReceiver
开始,unregisterReceiver
结束,并且会持有当前Context
,因此一定注意要成对使用,特殊情况Application除外。
出于这点的理解,监听发送频繁的广播会生成和销毁大量的对象?
为了验证这个问题写了下面这个小Demo:
<receiver
android:name=".receiver.StaticReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
</intent-filter>
</receiver>
class StaticReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
log("onReceive: $this")
}
}
I/common StaticReceiver: onReceive: com.whhhr.receiver.StaticReceiver@147451f
I/common StaticReceiver: onReceive: com.whhhr.receiver.StaticReceiver@762106c
I/common StaticReceiver: onReceive: com.whhhr.receiver.StaticReceiver@54a1635
I/common StaticReceiver: onReceive: com.whhhr.receiver.StaticReceiver@54dcaca
I/common StaticReceiver: onReceive: com.whhhr.receiver.StaticReceiver@825143b
可以看到和我们预料的一样,确实是每一次广播都会产生新的对象,因此应该避免使用静态注册的方式监听频繁调用的广播。
版本适配
由于静态广播的大量滥用,尤其是国产流氓APP,Android不得不一步步限制广播的使用。
Note: If your app targets API level 26 or higher, you cannot use the manifest to declare a receiver for implicit broadcasts (broadcasts that do not target your app specifically), except for a few implicit broadcasts that are exempted from that restriction. In most cases, you can use scheduled jobs instead.
Android 9
Beginning with Android 9 (API level 28), The
NETWORK_STATE_CHANGED_ACTION
broadcast doesn’t receive information about the user’s location or personally identifiable data.In addition, if your app is installed on a device running Android 9 or higher, system broadcasts from Wi-Fi don’t contain SSIDs, BSSIDs, connection information, or scan results. To get this information, call
getConnectionInfo()
instead.Android 8.0
Beginning with Android 8.0 (API level 26), the system imposes additional restrictions on manifest-declared receivers.
If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts (broadcasts that don’t target your app specifically). You can still use a context-registered receiver when the user is actively using your app.
Android 7.0
Android 7.0 (API level 24) and higher don’t send the following system broadcasts:
Also, apps targeting Android 7.0 and higher must register the CONNECTIVITY_ACTION broadcast using registerReceiver(BroadcastReceiver, IntentFilter). Declaring a receiver in the manifest doesn’t work.
在我写这篇文章的时候市面上主流Android版本已经是Android 12了,因此简单理解就是,现在不支持所有静态广播的注册,除了这些例外。
多次调用
对于动态注册,多次调用registerReceiver
,如果存在系统即时广播(刚绑定就会默认收到一条的情况)会响应多次onReceive
,但是不会创建多个Receiver对象,后续也只会响应一次onReceive
。
一些小记
针对系统广播,我本来以为所有广播常量都是像android.intent.action.BOOT_COMPLETED
这样集中记录在Intent
类中,但是以ACTION_NEXT_ALARM_CLOCK_CHANGED
为例发现在Intent
中没有记录,查了官网文档发现在AlarmManager
中,不知道为什么要怎么设计,总之一切还是要以官方为准。
public static final String ACTION_NEXT_ALARM_CLOCK_CHANGED =
"android.app.action.NEXT_ALARM_CLOCK_CHANGED";
广播清单
每个版本的广播都可以在sdk中如下目录找到:
{sdkDir/platforms/android-xx/data/broadcast_actions.txt}