原文链接:https://developer.android.com/guide/components/broadcasts.html

广播

Android应用可以向Android系统和其他Android应用发送或从它们那接收广播消息,这类似于发布-订阅设计模式。当需要关注的事件发生时这些广播就会发送出去。例如Android系统在许多系统事件发生时会发送广播,比如当系统启动或设备开始充电时。应用也可以发送自定义广播,例如通知其他APP他们关注的某些事(例如,下载了一些新数据)。

应用可以通过注册接收特定的一些广播。当广播发送出去,系统会自动将广播路由(routes)到订阅了该特定类型广播的应用程序。
一般来说,广播可以用作应用程序和非常规用户流之间的消息传递系统。但是,你必须注意不要滥用这个响应广播的机会,在后台运行一些会导致系统变慢的任务,正如下面这个视频描述的(译者注:略)。

系统广播

系统在许多系统事件发生时会自动发送广播,比如,当系统打开或关闭飞行模式。系统广播会发送给所有订阅了该事件的应用程序。
广播消息本身封装在一个Intent对象中,它的action字符串区分发生的事件(例如android.intent.action.AIRPLANE_MODE)。intent可能也包含其他包裹在扩展字段(extra field)中的信息。例如,飞行模式的itent包含一个boolean的扩展,它表明了飞行模式是否打开。
更多关于如何解读intents和从intent中获取action字符串,参见Intents and Intent Filterslink.
对于一个完整的系统广播action列表,参见Android SDK里的BROADCAST_ACTIONS.TXT文件。每个广播action有一个与之关联的常量字段。例如常量ACTION_AIRPLANE_MODE_CHANGED的值是android.intent.action.AIRPLANE_MODE。每个广播action的文档在其关联的常量字段中是可用的。

系统广播的变化

Android7.0以上不再发送以下系统广播。这项优化影响所有应用程序,不只针对那些Android7.0.
+ ACTION_NEW_PICTURE
+ ACTION_NEW_VIDEO

面向Android7.0以上的应用程序,下面这条广播必须使用registerReceiver(BroadcastReceiver, IntentFilter)注册,在manifest声明无效。
+ CONNECTIVITY_ACTION

从Android8.0(API 26)开始,系统对manifest声明的广播施加了其他限制。如果你的APP面向API26以上版本,对于大多数隐式广播(不是专门针对你的应用的广播),你不能使用manifest声明接收者。

接收广播

应用程序接收广播有两种途径:通过manifest声明接收者和context注册接收者。

Manifest声明的接收者

如果你在manifest中声明了一个广播接收者,系统会在该广播发送时启动你的应用(如果你的应用还没有运行).

注意:如果你的APP面向API26以上,你不能使用manifest为隐式广播(不是专门针对你的应用的广播)声明接收,除了一些不受限制的隐式广播。在大多数情况下,你可以计划任务代替。

按如下步骤在manifest中声明广播接收者:
1. 在你的应用的manifest中定义单元.

    <receiver android:name=".MyBroadcastReceiver"  android:exported="true"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/><action android:name="android.intent.action.INPUT_METHOD_CHANGED" /></intent-filter></receiver>
  1. 定义BroadcastReceiver 的子类,并实现onReceive(Context, Intent)方法。下面这个广播接收者实例打印并显示广播内容:
    public class MyBroadcastReceiver extends BroadcastReceiver {private static final String TAG = "MyBroadcastReceiver";@Overridepublic void onReceive(Context context, Intent intent) {StringBuilder sb = new StringBuilder();sb.append("Action: " + intent.getAction() + "\n");sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");String log = sb.toString();Log.d(TAG, log);Toast.makeText(context, log, Toast.LENGTH_LONG).show();}}

应用程序安装时,系统包管理者会注册这个接收者。接收者随即成为你的应用一个独立的入口点,这意味着在应用还没有启动时,系统可以启动该应用并传递广播。
系统创建一个新的BroadcastReceiver组件对象处理它接收的每一个广播。该对象仅在调用onReceive(Context, Intent)期间有效。一旦你的代码从该方法返回,系统就认为该组件不再活跃。

经Context注册的接收者

按如下步骤使用context注册接收者:
1. 创建[BroadcastReceiver](https://developer.android.com/reference/android/content
/BroadcastReceiver.html)的一个实例。

    BroadcastReceiver br = new MyBroadcastReceiver();

2. 创建一个IntentFilter,然后通过调用registerReceiver(BroadcastReceiver, IntentFilter)注册接收者。

    IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);this.registerReceiver(br, filter);

注意:要注册本地广播,调用LocalBroadcastManager.registerReceiver(BroadcastReceiver, IntentFilter) 代替。

Context注册的接收者在他们注册用的context有效期间接受广播。例如,如果在Activity context中注册,Activity没有销毁时你可以接收广播。如果你用Application context注册,你在整个应用运行期间可以接收广播。

3. 要停止接收广播,调用unregisterReceiver(android.content.BroadcastReceiver)。请确保在你不再需要它或者context不再有效的时候注销接收者。
注意你注册和注销接收者的地方,例如如果你使用activity的context在onCreate(Bundle)中注册接收者,你应该在onDestroy()中注销以防止将接收者泄漏在activity context之外。如果你在onResume()中注册接收者,你应该在onPause()中注销它以防止重复注册(如果你不想在paused时接收广播,这可以减少不必要的系统开销)。不要在onSaveInstanceState(Bundle)中注销,因为如果用户从历史栈中返回,该方法不会调用。

对进程状态的影响

广告接收者的状态(不管他有没有在运行)对它所在进程的状态有影响,这反过来又会影响它被系统杀死的可能性。例如,进程执行接收者(即,正在运行onReceive()中的代码时),它被视为一个前台进程。系统保持进程运行,除非在极度的内存压力情形下。
但是一旦你的代码重onReceive()返回,广播接收者就不再活跃。接收者的宿主进程变得和正在其中运行的其他app组件一样重要( The receiver’s host process becomes only as important as the other app components that are running in it).如果那个进程只持有一个manifest声明的接收者(应用程序的常见情形,用户从未或最近未与之交互),那么当从onReceive()返回时,系统认为它的进程是一个低优先级的进程,并且可能会将其杀死,以便为其他更重要的进程提供资源。
由于这个原因,你不应该在广播接收者中开启长时间运行的后台线程。onReceiver()之后,系统可能在任何时候杀死进程回收内存,并且在这过程中,他会终止运行在该进程中的线程。为了避免这样,你应该调用goAsync()(如果你想在后台线程中多花点时间处理处理广播)或者使用JobScheduler从接收者中安排一个JobService,这样系统知道该进程在继续执行积极的工作。更多信息请查看Processes and Application LifeCycle.

下面这个片段展示了一个使用goAsync()的BroadcastReceiver.它表明在onReceive()完成之后,它需要更多时间去完成工作。这在onReceive()中你要完成的工作时间长到足以引发UI线程从框架中断开(miss a frame >16ms)时尤其有用,这使得它更适合于后台线程。

public class MyBroadcastReceiver extends BroadcastReceiver {private static final String TAG = "MyBroadcastReceiver";@Overridepublic void onReceive(final Context context, final Intent intent) {final PendingResult pendingResult = goAsync();AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {@Overrideprotected String doInBackground(String... params) {StringBuilder sb = new StringBuilder();sb.append("Action: " + intent.getAction() + "\n");sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");Log.d(TAG, log);// Must call finish() so the BroadcastReceiver can be recycled.pendingResult.finish();return data;}};asyncTask.execute();}
}

发送广播

Android为应用程序发送广播提供三种途径:
+ sendOrderedBroadcast(Intent, String)方法发送广播一次给一个接收者。由于每个接收者按顺序执行,它可以将一个结果传递给下一位接收者,或者完全废弃这个广播,从而不会传给其他接收者。接收者的运行顺序可以通过对应的intent-filter的android:priority属性控制;相同优先级的接收者将以任意顺序运行。
+ sendBroadcast(Intent)方法以无序的形式给所有接收者发送广播。这称为普通广播。这效率更高,但意味着接收者不能从其他接收者那读取结果,传递从广播接收的数据,或者废弃广播。
+ LocalBroadcastManager.sendBroadcast方法发送广播给与发送者同一APP中的接收者。如果你不需要跨越APP发送广播,可使用本地广播。该实现更加高效,并且你无需担心由于其他app可以接收或发送你的广播而引起的安全问题。
下面的代码片段描述了怎样通过创建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字符串必须提供app的java包名语法,并唯一标志广播事件。你可以使用putExtra(String, Bundle)给intent附加其他信息。你也可以通过在intent上调用setPackage(String)将广播限制在同一组织的app集合。

注意:虽然intents同时用于发送广播和使用startActivity(Intent)启动activity,但这些action完全无关。广播接收者无法看到货捕捉启动一个activity的intent;同样地,当你广播一个intent,你无法找到或者无法启动一个activity.

使用权限限制广播

权限允许你将广播限制在持有特定权限的app集合。你可以对广播的发送者和接收者实施限制。

使用权限发送

当你调用sendBroadcast(Intent,String) 或者 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle),你可以指定一个权限参数。只有使用该标签在manifest中申请了该权限(如果是危险权限,还要经过允许)的接收者可以接收该广播,例如,下面的代码发送一条广播:

sendBroadcast(new Intent("com.example.NOTIFY"),Manifest.permission.SEND_SMS);

要接收该广播,接收方app必须像下面这样申请该权限:

<uses-permission android:name="android.permission.SEND_SMS"/>

你可以指定像SEND_SMS已经存在的系统权限或者使用自定义一个权限。有关权限和一般安全,请查看System Permissions.

注意:自定义权限在app安装时注册。定义自定义权限的app必须在使用它的app之前安装。

使用权限接收

如果你在注册广播接收者时指定了一个权限参数(registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)或manifest里的标签),那么只有在manifest中使用标签请求了该权限(如果是危险权限还要经过允许)的广播才能发送intent到该接收者。
例如,假设你的接收者app有一个manifest声明的接收者如下所示:

<receiver android:name=".MyBroadcastReceiver"          android:permission="android.permission.SEND_SMS"><intent-filter><action android:name="android.intent.action.AIRPLANE_MODE"/></intent-filter>
</receiver>

或者你的接收者app有一个context注册的接收者如下所示:

IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );

然后,为了能够发送广播给那些接收者,发送者app必须请求该权限,如下所示:

<uses-permission android:name="android.permission.SEND_SMS"/>

安全考虑和最佳实践

这里是一些安全考虑和发送和接收广播最佳实践:

Android官方文档翻译-Broadcasts相关推荐

  1. Android官方文档翻译 九 2.2Adding Action Buttons

    Adding Action Buttons 增加动作按钮 This lesson teaches you to 这节课教给你 Specify the Actions in XML 在XML中指定动作 ...

  2. android 官方说明文档,Android官方文档翻译-Accessibility

    标签元素 向用户提供解释每个可互动元素的意义和目的有用且形象的标签是非常重要的.这些标签允许屏幕阅读者(比如 TalkBack )正确向用户解释每个控制器的功能. 你可以使用一下两个方法提供元素的标签 ...

  3. Android官方文档翻译 十三 3.1Supporting Different Languages

    Supporting Different Languages 支持不同语言 This class teaches you to 这节课教给你 Create Locale Directories and ...

  4. android官方文档翻译,Android API Guide:Search 中文翻译

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 搜索,是安卓的一个核心功能.用户可以搜索他们所有能获得的数据,无论这些内容在设备上或者在网络上. Search Over ...

  5. Android官方文档翻译 十二 3.Supporting Different Devices

    Supporting Different Devices 支持不同设备 Dependencies and prerequisites 依赖关系和先决条件 Android 1.6 or higher A ...

  6. Android官方文档翻译 五 1.3Building a Simple User Interface

    Building a Simple User Interface 创建一个简单的用户界面 This lesson teaches you to 这节课将教给你: Create a Linear Lay ...

  7. Android官方技术文档翻译——Gradle 插件用户指南(5)

    昨晚把第五章未译完的几句话攻克了.只是第六章没怎么译,明后天又是周末,假设周一前第六章翻译完的话,周一再发第六章. 本文译自Android官方技术文档<Gradle Plugin User Gu ...

  8. Android官方技术文档翻译——Ant 任务

    本文译自Android官方技术文档<Ant Tasks>,原文地址:http://tools.android.com/tech-docs/ant-tasks. 因为是抽着时间译的,所以这篇 ...

  9. Android官方技术文档翻译——新构建系统概述

    本文译自Android官方技术文档<New Build System>,原文地址:http://tools.android.com/tech-docs/new-build-system. ...

最新文章

  1. [RHEL5企业级Linux服务攻略]--第6季 Vsftpd服务全攻略之高级配置
  2. 企业建设什么样的网站才能符合用户?
  3. 3DSlicer25:Report an Error
  4. WEB前端学习四 js什么是原始类型
  5. 深度学习(十一)——Winograd(1)
  6. python去除图片上的文字_Python图像处理之识别图像中的文字(实例讲解)
  7. 用C++实现十进制转二进制【个人思想】
  8. PyCharm中Scrapy的安装
  9. 自动驾驶——驾驶员反应时间的文献调研
  10. C++ STL算法系列5---equal() , mismatch()
  11. 开放源码软件的安装(tar包安装方法)
  12. 从微软Lync看企业办公通讯平台的演变
  13. 力软敏捷开发框架源码7.0.6解析
  14. MIDAS截面特性计算器说明
  15. Java二次开发海康SDK-对接门禁机
  16. 计算机刚启动时会显示,电脑启动时出现Windows错误恢复怎么解决?
  17. Zedboard 运行linaro操作系统
  18. wx.login 和 wx.getUserProfile 同时使用问题
  19. “降维打击”到底什么意思?
  20. jmeter入门学习记录

热门文章

  1. 操作系统实验(1)—— Linux命令解释程序设计与实现
  2. 安卓的工程目录文件夹简单介绍
  3. 阳阳智能开关教程从0到1目录导航
  4. 如何将多个bin文件合成一个bin文件?(二)
  5. 产品一:葡萄籽的美肌功效
  6. Tokenview | 区块链安全
  7. Doodle era
  8. 【机械】基于广义Hoek-Brown实现应变软化岩体GRC曲线及围岩位移应力塑性区绘制附matlab代码
  9. Paper intensive reading (三):Interactions Between Food and Gut Microbiota: Impact on Human Health
  10. 微信小程序使用mqtt mpvue mosquito