为啥android要使用各种broadcastreceiver

author author     2023-03-12     756

关键词:

作为Android四大组件之一的BroadcastReceiver(广播接收者),同Activity(活动)一样,经常被大家用到,网上也是一堆对它的讲解,那么为什么Android要用广播接收者这种机制呢?
广播分为:普通广播和有序广播
1.Normal broadcasts(普通广播):Normal broadcasts是完全异步的可以同一时间被所有的接收者接收到。消息的传递效率比较高。但缺点是接收者不能将接收的消息的处理信息传递给下一个接收者也不能停止消息的传播。可以利用Context.sendBroadcast发送。
2.Ordered broadcasts(有序广播):Ordered broadcasts的接收者按照一定的优先级进行消息的接收。一次传送到一个接收器。 随着每个接收器依次执行,它可以将结果传播到下一个接收器,或者它可以完全中止广播,使得它不会被传递到其他接收器。 命令接收器运行可以用匹配的意图过滤器的android:priority属性控制; 具有相同优先级的接收器将以任意顺序运行。可以利用Context.sendOrderedBroadcast发送。
官网上介绍广播是用的监听系统网络状况的例子,其实关键字在于“监听”。
(1) 创建广播接收者
BroadcastReceiver是一个抽象类,所以我们要创建自己的广播接收者就要继承它,继承后会有提示重写onReceive方法。
public class NetworkBroadcastReceiver extends BroadcastReceiver

@Override
public void onReceive(Context context, Intent intent)
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION))
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isAvailable())
Toast.makeText(context, "有网络连接", Toast.LENGTH_SHORT).show();
else
Toast.makeText(context, "无网络连接", Toast.LENGTH_SHORT).show();





广播接收者的生命周期是从接收广播开始,到onRecevier方法执行完成结束,时间很短,一般不允许处理大批量耗时操作。这里顺便给出打印NetworkInfo的信息以供参考:
NetworkInfo:
type: WIFI[,type_ext: WIFI],
state: CONNECTED/CONNECTED,
reason: (unspecified),
extra: "TP-LINK_EFE8",
roaming: false,
failover: false,
isAvailable: true,
isConnectedToProvisioningNetwork: false,
isIpv4Connected: true,
isIpv6Connected: false

[type: MOBILE[LTE],
state: CONNECTED/CONNECTED,
reason: connected,
extra: cmnet,
roaming: false,
failover: false,
isAvailable: true,
isConnectedToProvisioningNetwork: false]

(2) 静态注册广播
静态注册广播,需要在AndroidManifest.xml中,添加<recevier/> 标签,将广播接收者注册到应用中。要添加过滤器IntentFilter,由于系统网络变化时会发送ConnectivityManager.CONNECTIVITY_ACTION ("android.net.conn.CONNECTIVITY_CHANGE")的广播,所以我们要监听这条广播。
<receiver android:name=".NetworkBroadcastReceiver">
<intent-filter android:priority="1000">
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>

这里priority代表的是执行顺序的优先级,取值[-1000,1000],后面的有序广播会讲到。
(3) 动态注册广播
i.意图过滤器 IntentFilter 用于给BroadcastReceiver绑定监听广播类型
ii.自定义的BroadcastReceiver,例如上文的NetworkChangeBroadcastReceiver
iii.注册方法 Context.registerReceiver(Receiver, IntentFilter)
iv.反注册方法 unregisterReceiver(Receiver)
IntentFilter mFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
NetworkChangeBroadcastReceiver mReceiver = new NetworkChangeBroadcastReceiver();
registerReceiver(mReceiver, mFilter);

@Override
public void onDestroy()
super.onDestroy();
unregisterReceiver(mReceiver);


这段代码是成对出现的,可以在onCreate的时候注册,在onDestroy的时候反注册,也可以在onResume和onPause中执行这写方法。不过Google API推荐的做法,在activity的onResume()中注册,在onPause()反注册。效果是当界面pause时,就不接收广播,从而减少不必要的系统开销。还有就是一定要主动反注册你的广播,否则会出现异常。
动态注册和静态注册的差别:动态注册后,广播接收者会依赖Activity的生命周期,而静态注册的广播不会,只要是系统有发出的广播,它都会接收,与程序是否启动无关。
(4) 发送普通广播
具体使用的方法是sendBroadcast(Intent intent),通过隐式调用就可以,注意action是你自定义的,意思就是不可以发送系统广播,我试了,直接就崩了。
Intent intent = new Intent();
intent.setAction("com.fleming.chen.mybroadcast");
sendBroadcast(intent);

针对(3)(4)两点,如果你要用到的广播仅仅是应用里的,那么你可以用LocalBroadcastManager这个类,它与上述描述中的区别在于:
LocalBroadcastManager.getInstance(context).registerReceiver(mReceiver, mFilter);

LocalBroadcastManager.getInstance(context).unregisterReceiver(mReceiver);

LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

通过sendBroadcast发送的广播,不会被通过LocalBroadcastManager类注册的广播接收者接收,反之也是如此,两者是不可以”互通友谊“的,推荐使用LocalBroadcastManager来管理广播。
(5) 发送有序广播
上面讲了那么多都是普通广播,那什么又是有序广播呢?
有序广播关键在于这类广播是有序的,上文中提到priority,这是IntentFilter的属性,用来让不同的广播拥有不同的执行顺序,即优先级不同。
定义三种不同优先级的广播接收者:
public class MyBroadcastReceiver extends BroadcastReceiver

@Override
public void onReceive(Context context, Intent intent)
if (intent.getAction().equals("com.fleming.chen.myreceiver"))
String message = getResultData();
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
setResultData("这是修改后的数据");//第一个接收后处理一下,再交给下一个




public class MyBroadcastReceiver2 extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
if (intent.getAction().equals("com.fleming.chen.myreceiver"))
String message = getResultData();//得到上一个的处理结果
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
abortBroadcast();//主动停止广播,不再继续传下去




public class MyBroadcastReceiver3 extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
if (intent.getAction().equals("com.fleming.chen.myreceiver"))
//此时虽然该广播接收者也监听了,不过也没有内容
Toast.makeText(context, getResultData(), Toast.LENGTH_SHORT).show();




<receiver android:name=".MyBroadcastReceiver" >
<intent-filter android:priority="1000">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>
<receiver android:name=".MyBroadcastReceiver2">
<intent-filter android:priority="0">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>
<receiver android:name=".MyBroadcastReceiver3">
<intent-filter android:priority="-1000">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>

Intent intent = new Intent();
intent.setAction("com.fleming.chen.myreceiver");
sendOrderedBroadcast(intent, null, null, null, 0, "这是初始的数据", null);

对于广播的内容,在Android 7.0上做了修改,即Project Svelte:后台优化
Android 7.0 移除了三项隐式广播,以帮助优化内存使用和电量消耗。此项变更很有必要,因为隐式广播会在后台频繁启动已注册侦听这些广播的应用。删除这些广播可以显著提升设备性能和用户体验。
移动设备会经历频繁的连接变更,例如在 WLAN 和移动数据之间切换时。目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION 广播,让应用能够监控这些变更。由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。
同理,在之前版本的 Android 中,应用可以注册接收来自其他应用(例如相机)的隐式 ACTION_NEW_PICTURE 和 ACTION_NEW_VIDEO 广播。当用户使用相机应用拍摄照片时,这些应用即会被唤醒以处理广播。
为缓解这些问题,Android 7.0 应用了以下优化措施:
面向 Android 7.0 开发的应用不会收到 CONNECTIVITY_ACTION 广播,即使它们已有清单条目来请求接受这些事件的通知。在前台运行的应用如果使用 BroadcastReceiver 请求接收通知,则仍可以在主线程中侦听 CONNECTIVITY_CHANGE。
应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 广播。此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。
如果您的应用使用任何 intent,您仍需要尽快移除它们的依赖关系,以正确适配 Android 7.0 设备。Android 框架提供多个解决方案来缓解对这些隐式广播的需求。例如,JobScheduler API 提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。您甚至可以使用 JobScheduler 来适应内容提供程序变化。
所以说,在Android的世界,到处都充满着广播,就是为了用来监听手机的各种状态,给用户提醒,这是一种很好的用户体验,不过任何事情都是如此,广播也不可以多用哦,
参考技术A 监听广播的时候使用,例如开机自动启动

如果Android经常杀死应用程序,为啥要使用phonegap相机

】如果Android经常杀死应用程序,为啥要使用phonegap相机【英文标题】:WhyusephonegapcameraifAndroidfrequentlykillsapp如果Android经常杀死应用程序,为什么要使用phonegap相机【发布时间】:2013-04-0812:37:55【问题描述】:我目前正在开发一个P... 查看详情

Android,为啥要使用按钮视图,因为每个视图都可以有一个 onclicklistener

】Android,为啥要使用按钮视图,因为每个视图都可以有一个onclicklistener【英文标题】:Android,whyeveruseButtonviews,sinceeveryviewcanhaveanonclicklistenerAndroid,为什么要使用按钮视图,因为每个视图都可以有一个onclicklistener【发布时间】:20... 查看详情

为啥要使用环境变量对 JSON Web 令牌 (JWT) 进行签名?

】为啥要使用环境变量对JSONWeb令牌(JWT)进行签名?【英文标题】:WhyshouldJSONWebTokens(JWT)besignedwithanenvironmentvariable?为什么要使用环境变量对JSONWeb令牌(JWT)进行签名?【发布时间】:2016-02-1202:48:09【问题描述】:在各种教程中,我... 查看详情

android编程为啥要更新界面?怎么更新?

因为有时界面数据发生了变化,而界面没有跟着刷新,所以要更新界面。更新界面一般需要在主线程更新,可以避免多线程的麻烦,只有surfaceview可以在子线程里更新界面。至于怎么更新,只要在主线程调用view的各种有关数据或... 查看详情

为啥要使用三元运算符而不为“真”条件赋值 (x = x ?: 1)

...值(x=x?:1)【发布时间】:2011-02-1720:58:28【问题描述】:在Android开源qemu代码中我遇到了这行代码:mach 查看详情

为啥不继承 UIApplication?为啥要使用委托?

】为啥不继承UIApplication?为啥要使用委托?【英文标题】:WhynotsubclassUIApplication?Whyuseadelegate?为什么不继承UIApplication?为什么要使用委托?【发布时间】:2012-05-2604:47:09【问题描述】:除了使用应用程序委托以便UIApplication单例... 查看详情

android的硬件抽象层和驱动体系为啥这么弱暴

在各个厂商开发基于Android系统的产品的时候,虽然有的时候也需要修改Android的框架,但是移植是其中的主要工作。Android系统本身是一个庞大的系统,移植并不需要精通Android的每一个部分,需要考虑的是Android系统的硬件抽象层(... 查看详情

为啥要使用 ExpressionVisitor?

】为啥要使用ExpressionVisitor?【英文标题】:WhywouldIwanttouseanExpressionVisitor?为什么要使用ExpressionVisitor?【发布时间】:2017-05-1621:59:13【问题描述】:我从MSDN关于Howto:ModifyExpressionTrees的文章中知道ExpressionVisitor应该做什么。它应该... 查看详情

为啥我们要使用 sqlcommandbuilder?

】为啥我们要使用sqlcommandbuilder?【英文标题】:whywegoingforsqlcommandbuilder?为什么我们要使用sqlcommandbuilder?【发布时间】:2011-10-1908:31:22【问题描述】:选项1:在数据库中插入数据SqlCommandcmd=newSqlCommand("INSERTINTOtable_name(eid,eName,Dep... 查看详情

为啥要使用 SerializeField?

】为啥要使用SerializeField?【英文标题】:WhyshouldIuseSerializeField?为什么要使用SerializeField?【发布时间】:2018-11-0714:58:22【问题描述】:刚开始学习C#和Unity,有一点不习惯:为什么以及何时应该使用[SerializeField]?尽管使用了[Seri... 查看详情

为啥要使用hadoop

参考技术A感觉现在各个公司使用Hadoop的方式都不一样,主要我觉得有两种吧。第一种是longrunningcluster形式,比如Yahoo,不要小看这个好像已经没什么存在感的公司,Yahoo可是Hadoop的元老之一。这种就是建立一个DataCenter,然后有几... 查看详情

为啥要使用 jquery 地图?

】为啥要使用jquery地图?【英文标题】:Whyusingjquerymap?为什么要使用jquery地图?【发布时间】:2015-01-2713:16:45【问题描述】:为什么使用jquery.min.map如果:jquery=242kojquery.min+jquery.min.map=83+125=208ko(themapisevengreaterthanthelibrary)如果我们... 查看详情

为啥要使用 Objects.requireNonNull()?

】为啥要使用Objects.requireNonNull()?【英文标题】:WhyshouldoneuseObjects.requireNonNull()?为什么要使用Objects.requireNonNull()?【发布时间】:2018-01-1920:05:00【问题描述】:我注意到OracleJDK中的许多Java8方法使用Objects.requireNonNull(),如果给定... 查看详情

为啥要对jvm做优化?

摘要:在jvm中有很多的参数可以进行设置,这样可以让jvm在各种环境中都能够高效的运行。绝大部分的参数保持默认即可。本文分享自华为云社区《​​为什么需要对jvm进行优化,jvm运行参数之标准参数​​》,作者:共饮一杯... 查看详情

unity脚本类为啥要尽量避免继承monobehaviour类

参考技术A应该从MonoBehavior的机制来分析比较合适如果你的类无需引擎提供的各种初始化,更新及析构,物理,渲染等的回调.最好不要继承MonoBehavior继承后,引擎会在事件触发时,通过反射调用各种函数.这是需要消耗性能的.当然,如果... 查看详情

为啥要使用 Restify?

】为啥要使用Restify?【英文标题】:WhyshouldIuseRestify?为什么要使用Restify?【发布时间】:2013-07-0911:26:56【问题描述】:我需要在node.js中构建一个RESTAPI,并且正在寻找一个比express.js更轻量级的框架,它可能会避免不需要的功能... 查看详情

为啥要使用 WebPack?

】为啥要使用WebPack?【英文标题】:WhyuseWebPack?为什么要使用WebPack?【发布时间】:2019-08-1103:43:33【问题描述】:我花了几天时间让Webpack启动并运行,并且刚刚进行了测试。但是我发现webpack中的bundle.js文件做了很多对我来说没... 查看详情

Firebird 光标 - 你为啥要使用一个

】Firebird光标-你为啥要使用一个【英文标题】:FirebirdCursors-WhywouldyouuseoneFirebird光标-你为什么要使用一个【发布时间】:2016-05-2519:22:41【问题描述】:在文档here中,给出了使用cursor的以下代码示例:executeblockreturns(relationchar(31),s... 查看详情