Broadcasts详解
Android系统和Android应用程序都可以发送和接收广播,类似于publish-subscribe设计模式。广播在某个特定事件发生时被发送,例如,当各种系统事件发生时(如系统启动或设备开始充电),Android系统就会发送广播。应用程序也可以发送自定义广播,例如,通知其他应用程序他们关注的事件(如数据下载完成)。
应用程序可以注册广播接收器。当广播被发送时,系统自动将广播传递到已注册接收该广播的应用程序。一般来说,广播可以作为应用程序间、正常用户流之外的消息传递系统。
系统广播
当各种系统事件发生时,系统会自动发送广播,例如系统进入/退出飞行模式。系统广播被发送给所有注册该广播接收器的应用程序。广播消息被封装在一个Intent对象中,用Action字符串标识所发生的事件(例如android.intent.action.AIRPLANE_MODE
)。Intent还可能包含附加到其extra字段中的附加信息。例如,飞行模式的Intent包括一个boolean的附加值,指示当前是否处于飞行模式。
完整的系统广播Action清单,请查看Android SDK中的BROADCAST_ACTIONS.TXT
文件。每个广播Action都有一个与之相关联的常量字段。例如,常量字段ACTION_AIRPLANE_MODE_CHANGED
的值是android.intent.action.AIRPLANE_MODE
。
系统广播的变化
Android 7及以上版本不再发送以下系统广播。这种优化影响到所有应用程序,而不仅仅针对Android 7的应用程序。
- ACTION_NEW_PICTURE
- ACTION_NEW_VIDEO
Target为Android 7(API 24)及以上的应用程序要接收以下广播,必须在代码中动态注册,在manifest中静态注册将收不到广播。
- CONNECTIVITY_ACTION
从Android 8.0 (API 26)开始,系统对静态注册的广播接收器做了额外的限制。如果应用程序target为API 26及以上,则不能使用静态注册方式注册大多数隐式广播(不具体针对某个应用程序的广播)。
接收广播
应用程序有两种方式注册广播接收器:静态注册(在manifest中注册)和动态注册(在java代码中注册)。
静态注册广播接收器
如果你在manifest中注册一个广播接收器,且应用程序没有运行,系统就会在广播发送时启动你的应用程序。静态注册广播的步骤如下:
1、 在应用程序manifest中指定<receiver>
元素。
<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>
IntentFilter指定广播接收器关注的Action。
2、 继承BroadcastReceiver
类并实现onReceive(Context,Intent)
方法。下面示例中的广播接收器用log打印出了广播内容:
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();}
}
当安装应用程序时,System Package Manager
就注册了接收器。然后,广播接收器就成为应用程序的一个单独的入口点,即如果应用程序当前没有运行,系统可以通过广播启动应用程序。系统将创建一个新的BroadcastReceiver
组件对象来处理每个接收到的广播。这个对象在调用onReceive(Context, Intent)
的时间段内有效,一旦这个方法return,系统就认为该BroadcastReceiver组件不再active,并销毁它。
动态注册广播接收器
动态注册广播接收器的步骤如下:
1、 创建一个BroadcastReceiver的实例:
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);
只要注册广播的context有效,动态注册的广播接收器就能接收广播。例如,如果您用Activity的context注册广播接收器,则只要Activity未被销毁,就能接收广播。如果您用Application的context注册广播接收器,则只要APP在运行,就能接收广播。
3、 调用unregisterReceiver(android.content.BroadcastReceiver)
注销广播接收器。当你不再需要它,或context不再有效时,一定要注销接收器。
要注意注册和注销接收器的时机要配对,例如,如果你用Activity的context在onCreate(Bundle)
中注册接收器,那么要在onDestroy()
中注销接收器,以防止广播接收器泄露。如果Activity处于pause状态时你不想接收广播,那么你需要在onResume()
中注册接收器,在onPause()
中注销接收器,以防止异常的多次注册,这也可以减少不必要的系统开销。不要在onSaveInstanceState(Bundle)
中注销接收器,因为这个函数不是每次都被调用到。
对进程状态的影响
BroadcastReceiver是否运行影响其所在进程的状态,进而影响进程被系统杀死的可能性。例如,当一个进程正在运行一个广播接收器(即执行onReceive()
方法中的代码),那么它被认为是一个前台进程,它会保持正常运行,除开系统有极端内存压力。
然而,一旦你的代码从onReceive()
返回,BroadcastReceiver就不再active。一个常见现象:用户近期从未与应用交互过,如果这个进程中只运行了一个静态注册的Receiver,没有其它运行的组件,一旦代码从onReceive()
return,系统就会认为这是个低优先级的进程并可能会杀死它,使资源可用于其他更重要的进程。
因此,您不应该在BroadcastReceiver中启动长时间运行的后台线程。onReceive()
执行完后,系统可能杀死进程以随时回收内存,并终止运行在这个进程中的线程。为了避免这种情况,如果你想用更多时间在后台线程中处理广播,那么你应该调用goAsync()
或JobService
来让系统知道进程要继续执行一些工作。下面的代码显示了一个BroadcastReceiver使用goAsync()
标志onReceive()
返回后,它需要更多的时间来处理广播。
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为应用程序发送广播提供了三种方式:
1、 sendOrderedBroadcast(Intent, String)
:每次发送广播到一个接收器,每个接收器依次接收广播,并将结果传播给下一个接收器或终止广播。接收器运行的顺序可以通过IntentFilter的android:priority
属性来控制;具有相同优先级的接收器将以任意顺序运行。
2、 sendBroadcast(Intent)
:标准的广播发送方式,以随机顺序发送广播给所有接收器。这种广播更高效,但意味着接收器不能读取其他接收器的结果,也不能中止广播。
3、 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)
限制广播的接收者。
广播的权限设置
通过设置广播的权限,有特定权限的APP才能接收此广播。您可以对广播的发送方或接收方执行权限限制。
发送权限
当你调用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"/>
您可以指定一个现有的系统权限如SEND_SMS
,或用<permission>
元素自定义权限。有关权限和安全的一般信息,请参见 System Permissions。自定义权限是在安装应用程序时注册的。定义自定义权限的应用程序必须安装在使用它的应用程序之前。
接收权限
如果你注册Receiver时指定了一个权限参数,那么只有在manifest中用<uses-permission>
申请了权限的Broadcaster才能发送广播到这些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"/>
安全和性能
下面是发送和接收广播的一些安全和性能相关的注意事项:
1、 如果你不需要在应用程序之间发送广播,那么使用Support Library
库中的LocalBroadcastManager
发送广播。这种广播没有进程间通信,所有更高效,更安全。你不需要担心其他应用程序能够接收或发送你的广播导致的安全问题。本地广播可以在应用程序中作为通用的pub/sub event bus使用,而无需任何系统范围的广播开销。
2、 如果许多应用程序在其manifest中注册接收相同的广播,这可能导致该系统启动大量应用程序,从而对设备性能和用户体验造成重大影响。为了避免这种情况,尽量用动态注册替代静态注册。有时,Android系统本身强制使用动态注册接收器。例如,CONNECTIVITY_ACTION
广播仅发送给动态注册接收器。
3、 不要使用隐式Intent传播敏感信息。这些信息可以被注册接收广播的任何应用程序读取。有三种方法可以控制谁可以接收你的广播:
a、 可以在发送广播时指定权限。
b、 在Android4.0及以上系统上,发送广播时你可以用setPackage(String)
指定包名。系统将广播限制为与包名匹配的应用程序集。
c、 使用LocalBroadcastManager
发送本地广播
4、 当你注册一个接收器时,任何应用程序都可以向你的应用程序接收器发送潜在的恶意广播。有三种方法可以限制应用程序接收的广播:
a、 注册广播接收器时指定权限。
b、 对于静态注册的广播,把android:exported
设置为false
。Receiver将不接收来自应用程序外部的广播。
c、 可以用LocalBroadcastManager
限制自己只接收本地广播。
5、 广播Action的命名空间是全局的。确保Action名称是在您自己的命名空间中命名的,否则您可能会无意中与其他应用程序发生冲突。
6、 因为一个接收器的onReceive(Context, Intent)
方法在主线程上运行,它应该快速执行并返回。onReceive()
执行完后,系统可能杀死进程以随时回收内存,并终止了运行在这个进程中的线程。如果你想用更多时间在后台线程中处理广播,请用下面方法来完成:
a、 在onReceive()
中调用goAsync()
,并且传入BroadcastReceiver.PendingResult
。这会让Receiver在onReceive()
返回后保持Active。然而,即使采用这种方法,系统也希望您能在10秒内完成广播。它允许你把工作移动到另一个线程中,避免阻塞主线程。
b、 用JobScheduler
设置一个任务。更多信息请参考 Intelligent Job Scheduling。
7、不要从广播接收器中启动一个Activity,因为这样做的用户体验是不和谐的,特别是如果有多个接收器的话。相反,可以考虑显示通知。
Broadcasts详解相关推荐
- BroadcastReceiver详解
详解 2014-08-20 19:42 13492人阅读 评论(8) 收藏 举报 分类: 5.andriod开发(148) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 前言 ...
- 交换机ARP代理详解
交换机ARP代理详解 图表 The Host A (172.16.10.100) on Subnet A needs to send packets to Host D (172.16.20.200) ...
- ntp 服务端配置(/etc/ntp.conf配置详解) -小白实操记录
1 ntp 安装查看 1.1 安装命令 apt-get install ntp 1.2 查看服务是否启动 service --status-all 或者 ps -aux | grep ntp 1.3 ...
- linux查看端口及端口详解
今天现场查看了TCP端口的占用情况,如下图 红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说EST ...
- Android中的四大组件详解
Android中的四大组件详解 我们都知道Android系统应用层框架中,为开发者提供了四大组件来便于应用的开发,它们是Activity.Service.BroadcastReceiver.Conte ...
- Android 全局大喇叭——详解广播机制
Android 全局大喇叭--详解广播机制 一.广播机制简介 1. 标准广播(Normal broadcasts) 2. 有序广播(Ordered broadcasts) 二.接收系统广播 1. 动态 ...
- 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)
首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...
- JVM年轻代,老年代,永久代详解
秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...
- docker常用命令详解
docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...
最新文章
- Google工作原理
- 【转】python2与python3的主要区别
- 【数字信号处理】周期序列 ( 正弦序列特性 | 单个模拟周期采集 m 个数字样本 | Q 个模拟周期采集 P 个数字样本 | 非周期序列的情况 | 数字信号周期 )
- Android:在安卓中使用TFLite模型
- 企业级系统架构设计技术与互联网应用技术结合主题一 大规模并发性能问题探讨...
- 怎么打包图片_怎么将许多张照片打包发到邮箱?
- python xlwings 切片_Python xlwings库学习笔记(1)
- MTKI 驱动(57)---音频参数含义
- python3 ocr_python3 ocr 识别图片文字(CSDN验证码90%通过)
- MYSQL互为主从同步(Windows)
- 为什么判断list的时候,既要判断不等于null有要判断size大于0
- 自定义数组名做参数(传指针教学)
- python连接linux后一步一步的操作_Python使用技巧
- mongoDB导出数据库所有集合内容到json文件
- 寻找最小的k个数(四种方法)
- 最近学习安卓中总结的一些知识点
- 今天没发程序!HOHO!!!
- 还敢乱写代码??腾讯 Code Review 规范出炉!
- 天池大赛 xgboost/lightgbm + python36 + win10_64 环境配置
- 编写一个 spootboot 程序,有二个方法,一个问早上好,一个说再见