详解Android广播机制
应用场景(常见的场景1)
(1)同一应用具有多个进程的不同组件之间的消息通信
a)不同应用间的组件之间的消息通信
b)与Android系统在特定情况下的通信,如:系统开机,网络变化等
(2)同一应用内同一组件的消息通信:显然扩展变量的作用域、接口回调、Handler-Message等方式都能更简单的实现。
(3)同一应用内的不同组件之间的消息通信(单个进程):对于简单的的情况,依靠接口的回调方式就可解决;而较为复杂的情况,更推荐直接使用EventBus等。
实现原理
设计模式与模型: Android中的广播使用了观察者模式,模型为 基于消息的发布/订阅事件模型。
从设计模式上讲,广播的发送者和接收者极大程度的解耦,使得系统方便集成,容易扩展
模型成员:
- 消息发布者(广播发布者)
- 消息订阅者(广播接收者)
- 消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)
此处我们扩展一下,观察者模式和发布订阅模式的关系
- 发布订阅模式属于广义上的观察者模式,前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式,在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
- 发布订阅模式加入消息中心,实现发布者和订阅者的解耦:在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。
实现流程
- 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
- 广播发送者通过binder机制向AMS发送广播;
- AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver
- AMS将广播发送到上述符合条件的BroadcastReceiver相应的消息循环队列中
- BroadcastReceiver通过消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。
BroadcastReceiver
自定义BroadcastReceiver,继承基类BroadcaseReceiver,实现抽象方法onReceive(context, intent)收到广播后,会自动回调onReceive(..)方法,通常,onReceive(..)方法会涉及到与其他组件的交互,如发送Notification,启动service等。默认情况,BroadcaseReceiver运行在UI线程,因此,onReceive(..)方法不能执行耗时操作,否则ANR
简单的自定义Demo:
MyBroadcastReceiver.java
//继承BroadcastReceiver基类
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
@Override
public 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注册类型
1. 静态注册
- 在
AndroidManifest.xml
文件中通过<receiver>
进行注册 - 规则及实例说明:
<receiver
//BroadcastReceiver子类的类名
android:name="string" //是否使用该BroadcastReceiver
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:permission="string" //broadcastReceiver运行所处的进程。
//默认为app的进程,可以指定独立的进程
//Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string"> //指定此广播接收器将用于接收特定的广播类型
//本例中给出的时系统开机后自身发出的广播
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
以上述静态方法注册的MyBroadcastReceiver
,在app
首次启动时,系统或自动实例化MyBroadcastReceiver
,并注册到系统中。
2. 动态注册
- 在代码中调用
Context.registerReceiver()
, - 典型写法示例如下:
public class MainActivity extends AppCompatActivity { public static final String BROADCAST_ACTION = "com.example.whd_alive";
private BroadcastReceiver mBroadcastReceiver; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化MyBroadcastReceiver
mBroadcastReceiver = new MyBroadcastReceiver();
//实例化IntentFilter
IntentFilter intentFilter = new IntentFilter(); //设置接收广播的类型
intentFilter.addAction(BROADCAST_ACTION); //动态注册
registerReceiver(mBroadcastReceiver, intentFilter);
} //销毁广播
//当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
//当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mBroadcastReceiver);
}
}
注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回调需要unregisterReceiver(mBroadcastReceiver)。
广播发送及广播类型
广播发送,广播 这一实体本身以 intent 表示,广播的定义 = 相应广播intent的定义,广播的发送:通过广播发送者将此intent发送出去,根据不同类型的广播调用相对应的send方法。
广播的类型主要分为一下四类:
- Normal Broadcast(普通广播):通常调用sendBroadcast(Intent)(Intent, String)方法发送
- System Broadcast(系统广播):发生各种事件时,系统自动发送
- Ordered Broadcast(有序广播):调用sendOrderedBroadcast(Intent, String)方法发送
- Local Broadcast(本地广播):调用LocalBroadcastManager.sendBroadcast(intent)方法发送
- Sticky Broadcast(粘性广播):已弃用(API 21)
1. Normal Broadcast(普通广播)
开发者自定义的intent,以Context.sendBroadcast(),Context.sendBroadcastAsUser()等方法发送该intent。
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
//最普通的发送方式
sendBroadcast(intent);
//附带权限的发送方式,声明此权限的BroadcastReceiver才能接收此广播
sendBroadcast(intent,RECEIVER_PREMISSION); //以下两种不常见,是因为只有预装在系统映像中的程序才能使用,否则无法使用
//指明接收人的发送方式
sendBroadcastAsUser(intent,USER_HANDLER);
//指明接收人以及对应权限的发送方式
sendBroadcastAsUser(intent,USER_HANDLER,RECEIVER_PREMISSION);
- 若被注册了的
BroadCastReceiver
注册的intentFilter
的action
与上述匹配,则会接收此广播,且顺序是无序的。如果发送时有相应的权限要求,则BroadCastReceiver
只有拥有相应的权限才能接受。
<receiver
android:name=".MyBroadcastReceiver"
android:permission="RECEIVER_PREMISSION">
<intent-filter>
<action android:name="BROADCAST_ACTION"/>
</intent-filter>
</receiver>
2. System Broadcast(系统广播)
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。
3. Ordered Broadcast(有序广播))
发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的有序广播中的“有序”是针对广播接收者而言的
发送方式:
定义过程与普通广播一样,调用sendOrderedBroadcast(),同样也有对应的sendOrderedBroadcastAsUser()方法,只不过同样针对于预装在系统映像的应用。
特点
按顺序接收
允许优先级高的BroadcastReceiver截断广播。
允许优先级高的BroadcastReceiver修改广播
接受顺序
priority值不同:由大到小排序
priority值相同:动态注册优于静态注册
4. Local Broadcast(本地广播)
可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App
方案2的具体实现:
使用封装好的LocalBroadcastManager类。
使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。
对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)
代码示例如下:
//实例化MyBroadcastReceiver
mBroadcastReceiver = new MyBroadcastReceiver();
//实例化IntentFilter
IntentFilter intentFilter = new IntentFilter(); //得到LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); //设置接收广播的类型
intentFilter.addAction(BROADCAST_ACTION); //动态注册
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter); //取消注册
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型
- 静态注册(全局+本地): 回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext
- 全局动态注册: 回调onReceive(context, intent)中的context具体指的是Activity Context;
- LocalBroadcastManager动态注册,回调onReceive(context, intent)中的context具体指的是Application Context。
出于安全考虑的广播使用最佳实践
如不需要向应用程序之外的组件发送广播,则可以使用支持库Support Library中LocalBroadcastManager发送和接收本地广播。如果许多应用程序清单中注册接收相同的广播,它会导致系统启动大量的应用程序,从而对设备性能和用户体验产生重大影响。为了避免这种情况,请使用动态注册而不是Manifest声明。有时,Android系统本身会强制使用上下文注册的接收器。例如,CONNECTIVITY_ACTION广播只允许动态注册。
onReceive(Context, Intent)运行在UI线程,不要进行耗时操作
如耗时操作必不可少,生成子线程。
不要使用隐含的意图传播敏感信息。这些信息可以被任何注册的应用程序读取。
解决方案 : permission / setPackage(String) / LocalBroadcastManager.
当注册一个BroadcastReceiver,任何应用程序都可以发送潜在的恶意广播到你的应用的BroadcastReceiver。
解决方案 : permission / android:exported = "false" / LocalBroadcastManager.
广播操作的命名空间是全局的。确保操作名称和其他字符串都是在您自己的名称空间中编写的,否则您可能会无意中与其他应用程序发生冲突。
不要从BroadcastReceiver开始活动,这么做会导致用户体验很差,特别是如果有不止一个BroadcastReceiver。相反,考虑使用Notification。
————————————————
版权声明:本文为CSDN博主「whd_Alive」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whdAlive/article/details/80250261
详解Android广播机制的更多相关文章
- Android随笔之——Android广播机制Broadcast详解
在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理.这个广播跟我们传统意义中的电台广播有些相似之处.之所以叫做广播,就 ...
- 图文详解 Android Binder跨进程通信机制 原理
图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...
- Android Binder IPC详解-Android学习之旅(96)
linux内存空间与BInder Driver Android进程和linux进程一样,他们只运行在进程固有的虚拟空间中.一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间 ,用户空间是 ...
- Android广播机制的深入学习
部分内容转载自http://www.cnblogs.com/lwbqqyumidi/p/4168017.html 1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者 ...
- Android总结篇系列:Android广播机制
1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通 ...
- Android广播机制概述
1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通 ...
- 详解Android首选项框架ListPreference
详解Android首选项框架ListPreference 原文地址 探索首选项框架 在深入探讨Android的首选项框架之前,首先构想一个需要使用首选项的场景,然后分析如何实现这一场景.假设你正在编写 ...
- Android广播机制:Broadcast
转载:Android总结篇系列:Android广播机制 1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广 ...
- 详解浏览器缓存机制与Apache设置缓存
一.详解浏览器缓存机制 对于,如何说明缓存机制,在网络上找到了两张图,个人认为思路是比较清晰的.总结时,上图. 这里需要注意的有两点: 1.Last-Modified.Etag是响应头里的数据 2.I ...
随机推荐
- 修改Ubuntu16.04默认主题标题栏的颜色
默认主题为Ambiance: sudo gedit /usr/share/themes/Ambiance/gtk-3.0/gtk-main.css 将: @define-color dark_bg_c ...
- 全球首个开放应用模型 OAM 开源
业界要闻 全球首个开放应用模型 OAM 开源 2019 年 10 月 17 日,阿里巴巴合伙人.阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 Qcon 上海重磅宣布,阿里云与微软联合推出开放应 ...
- 累乘函数线性逆元打表,阶乘反演——bzoj4816
学了一种新套路,倒序打表函数的逆元可以直接线性完成 #include<bits/stdc++.h> using namespace std; #define ll long long #d ...
- 好用的日期控件jeDate
最近做公司后台系统关于仓库的一些东西,需要根据时间范围来导出一些数据,我们使用的后台框架是基于bs的,bs也有时间控件:bootstrap-datepicker是只能选择日期的, daterangep ...
- Delphi中任务栏状态区的编程
在Windows桌面的任务栏上有一个凹陷的区域,其中显示着系统时钟以及一些图标,这个长方形的区域便是Windows的任务栏状态区(taskbar status area).本文将介绍使用Borland ...
- Intergalactic Map SPOJ - IM
传送门 我觉得我写得已经和题解一模一样了,不知道为什么就是过不了..懒得拍了,反正不是很难,不太想浪费时间. 1~2~3的一条路径相当于从2~1的一条路径+2~3的一条路径,点不能重复经过,于是拆点. ...
- 微信H5授权登陆
Controllerpackage com.iimscloud.auth.provider.controller; import org.springframework.beans.factory.a ...
- 牛客多校第十场 F Popping Balloons 线段树维护稀疏矩阵
题意: 给定一个稀疏矩阵,里面有若干个气球,让你横着开三枪,竖着开三枪,问最多能打爆多少气球,要求相同方向,相邻两枪必须间隔r. 题解: 横向记录每列有多少个气球,分别在哪行上. 然后把这个数据改造成 ...
- kubernetes session and 容器root权限
session保持 如何在service内部实现session保持呢?当然是在service的yaml里进行设置啦. 在service的yaml的sepc里加入以下代码: sessionAffinit ...
- tensorflow 模型加载(没有checkpoint文件或者说只加载其中一个模型)
1.如果有checkpoint文件的话,加载模型很简单: 第一步:都是加载图: with tf.Session() as sess: saver=tf.train.import_meta_graph( ...