1、定义
BroadcastReceiver即广播,属于Android四大组件之一
广播分:广播发送者、广播接收者
2、作用
监听其它应用/系统应用发出的消息,自身做出响应。
3、应用场景
1、不同应用之间通信;
2、应用内通信;
3、获取系统一些状态变化;
4、实现原理
广播使用观察者模式,基于消息发布/订阅事件模型
5、广播注册方式
注册方式分:静态注册、动态注册
静态注册:在AndroidManifest.xml里通过****标签声明
此 App
首次启动时,系统会自动实例化mBroadcastReceiver
类,并注册到系统中。
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
动态注册:
在代码中调用Context.registerReceiver()
方法
对于动态广播,有注册就必然得有注销,否则会导致内存泄露
- 不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
当系统因为内存不足(优先级更高的应用需要内存)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。 - 假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
- 但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。
6、广播类型
普通广播、系统广播、有序广播、App内广播(LocalBroadcastManager类)
按发送方式有:
显示广播:
Intent指定了组件名称(setPackage()或setComponent()),这个Intent为显式Intent,用它发送的广播为显式广播。广播接收者直接就是指定的组件名称对应的广播接收者。
Intent intent = new Intent();
//同时显式指定组件名
intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
context.sendBroadcast(intent)
隐式广播:
Intent指定action, 这个Intent则为隐式Intent,使用它发送的广播则为隐式广播。系统通过IntentFilter去查找的隐式广播接收者。
示例
//隐式intent,发送隐式广播
Intent intent = new Intent("com.demo.recriver");
context.sendBroadcast(intent);
Android8.0限制隐式广播的关键代码逻辑如下 (BroadcastQueue.java)
} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
&& r.intent.getPackage() == null
&& ((r.intent.getFlags()
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
&& !isSignaturePerm(r.requiredPermissions))) {
mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
component.getPackageName());
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent + " to "
+ component.flattenToShortString());
skip = true;
}
}
}
Android8.0后如何接收开机广播?
1)并配置了白名单
源码目录/frameworks/base/data/etc/framework-sysconfig.xml
if (action != null) {
if (getBackgroundLaunchBroadcasts().contains(action)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
}
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
2)系统为白名单添加FLAG_RECEIVER_INCLUDE_BACKGROUND的标志位;
3)接收方静态注册开机广播接收器;
7、特别注意
对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
- 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
- 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
- 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
- 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
-
进程生命周期错误的一个常见示例是当
BroadcastReceiver
在其BroadcastReceiver.onReceive()
方法中接收到一个 Intent 时,它会启动一个线程,然后从该函数返回。一旦返回,则系统会认为 BroadcastReceiver 不再处于活动状态,因此不再需要其托管进程(除非其中有其他应用组件处于活动状态)。因此,系统可能会随时终止进程以回收内存,这样会终止在进程中运行的衍生线程。要解决这个问题,通常可以从 BroadcastReceiver 调度JobService
,这样系统就知道进程中还有处于活动状态的任务正在进行中。