一、Android 广播机制包括 3 个基本要素:
发送广播的 Broadcast;
接受广播的 BroadcastReceiver;
用于传递信息的 Intent;
二、发送广播
Android 为应用程序发送广播提供了三种方式:
2.1 sendOrderedBroadcast(Intent, String) — 有序广播
sendOrderedBroadcast(Intent, String)
方法一次向一个接收器发送广播。当每个接收器依次执行时,它可以将结果传播到下一个接收器,它也可以完全中止广播,使其不会传递给其他接收器。receiver 的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性进行控制;具有相同优先级的接收器将以任意顺序运行。
2.2 sendBroadcast(Intent) — 普通广播
sendBroadcast(Intent)
方法以随机的顺序向所有接收者发送广播。这被称为普通广播。这样更有效率,但意味着 receiver 无法读取其他 receiver 的结果、传播从其他广播接收的数据以及中止广播。
2.3 LocalBroadcastManager.sendBroadcast — 本地广播
LocalBroadcastManager.sendBroadcast()
方法将广播发送到与发送者位于同一应用程序中的接收者。如果你不需要跨应用发送广播,请使用本地广播。实现效率更高(不需要进程间通信),而且无需担心其他应用程序能够接收或发送你的广播。
以下代码片段演示了如何通过创建一个 Intent 并调用 sendBroadcast(Intent) 来发送广播。
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);
广播消息被包裹在一个 Intent 对象中。Intent 的 action 字符串必须提供应用程序的 Java 包名称并唯一标识广播事件。你可以将附加信息通过 putExtra(String, Bundle)
添加到 intent 中。你也可以通过调用 setPackage(String)
限制发送的应用程序。
注意:尽管 intent 用于发送广播和启动活动
(startActivity(Intent))
,但这些操作完全不相关。
三、使用权限限制广播
权限允许你将广播限制为拥有特定权限的应用程序集。你可以对广播的发送者或接收者实施限制。
3.1 带权限发送
当你调用 sendBroadcast(Intent, String)
或者 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
,你可以指定一个权限参数。只有接收方已经在 manifest 中标记了权限(并且授予许可,如果该权限存在危险)才可以接收广播。例如,下面的代码发送一个广播:
sendBroadcast(new Intent("com.example.NOTIFY"),
Manifest.permission.SEND_SMS);
要接收广播,应用程序必须请求权限,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
注意:自定义权限是在安装应用程序时注册的。定义自定义权限的应用程序必须在使用之前安装。
3.2 带权限接收
如果你在注册 receiver 时指定一个权限参数(无论是在 manifest 的 <receiver> 标签中,还是通过 registerReceiver(BroadcastReceiver, IntentFilter, String, Handler))
,那么只有在 manifest 中通过 <uses-permission> 标签请求该权限的应用程序 (并且授予许可,如果该权限存在危险)可以发送 intent 给该 receiver。
例如,假设你的接收应用程序具有如下所示的接收器声明:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
或者你的接收应用程序有一个上下文注册的接收器,如下所示:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
那么,为了能够向这些接收方发送广播,发送方必须请求权限,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
四、安全考虑和最佳做法
以下是发送和接收广播的一些安全考虑事项和最佳做法:
如果你不需要将广播发送到你的应用以外的组件,则可以使用
LocalBroadcastManager
发送和接收本地广播。LocalBroadcastManager
效率要高得多(不需要进程间通信),并可以让你避免其他应用程序能够接收或发送你的广播。本地广播可以在你的应用程序中作为通用的 pub/sub event bus,而无需系统广播的任何开销。如果许多应用程序已经在 manifest 中注册相同的广播,则可能会导致系统启动大量应用程序,从而对设备性能和用户体验产生重大影响。为了避免这种情况,优先使用 context 方式注册广播。有时,Android 系统本身会强制使用上下文注册的接收器。例如,
CONNECTIVITY_ACTION
广播仅传送给上下文注册的接收器。-
不要使用隐式 intent 传播敏感信息。任何注册了接收该广播的应用程序都可以读取信息。有三种方法可以限制哪些应用能够接收你的广播:
可以在发送广播时指定权限。
在 Android 4.0 及更高版本,可以通过
setPackage(String)
指定包名。可以使用
LocalBroadcastManager
发送广播。
-
当你注册 receiver 时,任何应用程序都可能将潜在的恶意广播发送到你应用的 receiver。有三种方法可以限制你的应用可以收到哪些广播:**
可以在注册广播接收器时指定权限。
对于 manifest 声明的 receiver,可以在清单中将
android:exported
属性设置为 “false”。这样 receiver 就不接收来自应用程序外部的广播。可以通过
LocalBroadcastManager
限制只有本地广播。
广播动作 (action) 的命名空间是全局的。确保动作名称和其他字符串被写入您拥有的名称空间中,否则可能会无意中与其他应用程序发生冲突。
-
因为 receiver 的
onReceive(Context, Intent)
方法在主线程上运行,所以它应该快速执行并返回。如果你需要执行长时间运行的工作,请注意开启子线程或启动后台服务,因为系统可能会在onReceive()
返回后终止整个进程 。建议:在 receiver 的
onReceive()
方法中调用goAsync()
方法并传递BroadcastReceiver.PendingResult
给后台线程。这使广播从onReceive()
返回后依旧保持活动状态。但是,即使采用这种方法,系统也希望你能够很快结束广播(不到 10 秒)。不过,它确实允许你将工作移至另一个线程以避免妨碍主线程。使用
JobScheduler
安排工作。
不要从广播接收器启动活动,因为很影响用户体验;特别是如果有多个接收器的话。相反,请考虑执行显示通知等简单操作。