广播接受者(Broadcast Receiver)

2024-04-28 11:08

本文主要是介绍广播接受者(Broadcast Receiver),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

监听设备的重启

设备重启后,那些持续运行的应用通常也需要重启。通过监听具有BOOT_COMPLETED操作的broadcast intent,可得知设备是否已完成启动。

创建Broadcast Receiver
package com.example.photogallery;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class StartupReceiver extends BroadcastReceiver {private static final String TAG = "StartupReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, intent.getAction());}}

broadcast receiver须在manifest配置文件中登记(也可在代码中登记),类似于activity或service。使用receiver标签并包含相应的intent-filter在其中。StartupReceiver 将会监听BOOT_COMPLETED操作,该操作还需配置RECEIVE_BOOT_COMPLETED使用权限。

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/><receiver android:name="com.example.photogallery.StartupReceiver" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>

与activity和service不同,在配置文件中声明的的broadcast receiver几乎总是需要声明intent-filter。broadcast intent就是为发送信息给多个监听者而生的,但显示intent只有一个receiver。因此显示broadcast intent很少见。

在配置文件中完成声明后,即使应用当前并未运行,只要有匹配的broadcast intent的发来,broadcast receiver就会接收。一收到intent,broadcast receiver的onReceive(Context, Intent)方法即开始运行,然后broadcast receiver就会被销毁。

如何使用Broadcast Receiver

由于onReceive(Context, Intent)方法运行在住线程上,因此不能再该方法内做一些耗时的重度任务,如连接网络或数据的永久存储等。

receiver需要知道定时器的启停状态。在PollService类中添加一个Preferences常量,用于存储状态信息。

    public static void setServiceAlarm(Context context, boolean isOn) {....
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(PREF_IS_ALARM_ON, isOn).commit();}

设备重启后启动定时器

    @Overridepublic void onReceive(Context context, Intent intent) {Log.i(TAG, intent.getAction());boolean isOn = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PollService.PREF_IS_ALARM_ON, false);PollService.setServiceAlarm(context, isOn);}

发送与接受自己的广播

通过发送与接受自己的广播,避免在打开应用后,我们仍能接收都通知信息。

发送广播
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
动态broadcast receiver

我们可以编写一个类似StartupReceiver,并在manifest配置文件中登记的broadcast receiver来接收intent。但这里该方法行不通。我们须在PhotoGalleryFragment存在的时候接收intent。而在manifest配置文件中声明的独立receiver则很难做到这一点。因为该receiver在不断接收intent的同时,还需要另一种方法来知晓PhotoGalleryFragment的存在状态。

使用动态broadcast receiver可解决该问题。动态broadcast receiver是在代码中,而不是在配置文件中完成登记声明的。

创建一个隐藏前台通知的通用fragment

package com.example.photogallery;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.app.Fragment;
import android.widget.Toast;public abstract class VisibleFragment extends Fragment {private static final String TAG = "VisibleFragment";private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(getActivity(),"get a broadcast: " + intent.getAction(),Toast.LENGTH_SHORT).show();}};@Overridepublic void onResume() {super.onResume();/*** 这里创建的IntentFilter与在配置文件中定义的intent-filter是一样的。任何使用XML定义的IntentFilter,均可以* 代码的方式完成定义。要配置以代码方式创建的IntentFilter,直接调用addCategory(String)、addAction(String)和* addDataPath(String)等方法。*/IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);getActivity().registerReceiver(mOnShowNotification, filter);}@Overridepublic void onPause() {super.onPause();/*** 使用完后,动态登记的broadcast receiver必须能够自我清除。通常,如果在启动生命周期方法中登记了receiver,* 则需在相应的停止方法中调用Context.unregisterReceiver(BroadcastReceiver)方法。因此,这里,我们* 在onResume()方法里登记,而在onPause()方法里撤销登记。同样的,如果在onActivityCreated()方法里登记,* 则应在onActivityDestoryed(...)方法里撤销登记。* 顺便要说的是,我们应注意保留fragment中的onCreate(...)和onDestory()方法的应用。设备发生旋转时,* onCreate(...)和onDestory()方法中的getActivity()方法会返回不同的值。因此,想在Fragment.onCreate(...)* 和Fragment.onDestory()方法中实现登记或撤销登记,应使用getActivity().getApplicationContext()方法。* */getActivity().unregisterReceiver(mOnShowNotification);}}

修改PhotoGalleryFragment,调整其父类为VisibleFragment。

使用私有权限

使用动态broadcast receiver存在一个问题,即系统中的任何应用均可监听并触发我们的receiver。

不要担心,有多种方式可用于阻止未授权的应用闯入我们的私人领域。如果receiver声明在manifest配置文件里,且权限应用内部使用,则可在receiver标签上添加一个android:exported=”false”属性。这样系统中的其他应用就再也无法接触到该receiver。另外,也可创建自己的权限。这通常通过在manifest配置文件添加一个permission标签来完成。

<permission android:name="com.example.photogallery.PRIVATE"android:protectionLevel="signature"/><uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="com.example.photogallery.PRIVATE"/>

发送带有权限的broadcast

public static final String PERM_PRIVATE="com.example.photogallery.PRIVATE";sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);

以上代码中,使用protectionLevel签名,我们定义了自己的定制权限。如同intent操作、类别以及系统权限,权限本身只是一行简单的字符串。即使是自定义的权限,也必须在使用前获取它,这是规则。三处出现的字符串“com.example.photogallery.PRIVATE”必须保证完全一致。

要使用权限,必将其作为参数传入sendBroadcast(…)方法里,指定了接收权限,任何应用必须使用同样的权限才能接收我们发送的intent。

要怎么保护我们的broadcast receiver呢?其他应用可通过创建自己的broadcast receiver来触发broadcast receiver。同样,在registerReceiver(…)方法中传入自定义权限即可解决该问题。

    public void onResume() {super.onResume();IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);getActivity().registerReceiver(mOnShowNotification, filter, PollService.PERM_PRIVATE, null);}
深入学习protectionLevel

自定义权限必须指定android:protectionLevel属性值。Android根据protectionLevel属性值确定自定义权限的使用方法。

protectionLevel的可选值
这里写图片描述

使用ordered broadcast接收结果

从概念上讲,常规broadcast intent可同时被其他应用所接收。而现在,onReceive(…)方法是在主线程上调用的,所以实际上,receiver并没有同步并发运行。因而,指望它们会按照某种顺序依次运行,或知道它们什么时候全部结束运行也是不可能的。结果,这给broadcast receiver之间的通讯,或intent发送者接收receiver的信息都带来了麻烦。

为解决问题,可使用有序broadcast intent实现双向通讯。有序broadcast允许多个broadcast receiver依次处理broadcast intent。另外,通过传入一个名为result receiver的特别broadcast receiver,有序broadcast还可以实现让broadcast的发送者接收broadcast接受者发送的返回结果。

从接收方来看,这看上去与常规broadcast没有什么不同。然而,这里我们获得了一个特别工具:一套改变接受者返回值的方法。这里我们需要取消通知信息。可通过一个简单的整数结果码,将此需要告知信息发送者。使用setResultCode(int)方法,设置结果码为Activity.RESULT_CANCELED。

修改VisibleFragment类,将取消通知的信息发送给SHOW_NOTIFICATION的发送者。

private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {setResultCode(Activity.RESULT_CANCELED);}};

既然此处只需发送YES或NO的标志,因此使用int结果码即可。如需返回更多复杂数据,可使用setResultData(String)或setResultExtras(Bundle)方法。如需设置所有三个参数,可调用setResult(int, String, Bundle)方法。设定返回值后,每个后续接受者均可看到或修改返回值。

为让以上方法发挥作用,broadcast必须有序。在PollService类中,编写一个可发送有序broadcast的新方法。该方法打包一个Notification调用,然后作为一个broadcast发出。只要通知信息还没有被撤销,可指定一个result receiver发出打包的Notification。

    void showBackgroundNotification(int requestCode, Notification notification) {Intent intent = new Intent(ACTION_SHOW_NOTIFICATION);intent.putExtra("RESULT_CODE", requestCode);intent.putExtra("NOTIFICATION", notification);/*** 除了sendBroadcast(Intent, String)方法中使用的参数外,sendOrderedBroadcast(Intent,* String, BroadcastReceiver, Handler, int, String, Bundle)方法还有另外5个参数,依次为:* 一个result receiver、一个支持result receiver运行的Handler、结果代码初始化值、结果数据以及有序* broadcast的结果附加内容。*/sendOrderedBroadcast(intent, PERM_PRIVATE, null, null,Activity.RESULT_OK, null, null);}

result receiver比较特殊,只有在所有有序broadcast intent的接受者结束运行后,它才开始运行。虽然有时可使用result receiver接收broadcast和发送通知对象,但此处该方法行不通。目标broadcast intent通常是在PollService对象消亡前发出的,也就是说broadcast receiver可能也被销毁了。

因此,最终的broadcast receiver需要保持独立运行。新建一个NotificationReceiver类。

package com.example.photogallery;import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;public class NotificationReceiver extends BroadcastReceiver {private static final String TAG = "NotificationReceiver";@Overridepublic void onReceive(Context context, Intent intent) {if (getResultCode() != Activity.RESULT_OK)return;int requestCode = intent.getIntExtra("RESULT_CODE", 0);Notification notification = (Notification) intent.getParcelableExtra("NOTIFICATION");NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);/*** 传入的整数参数是通知消息的标识的标识符,在整个应用中该值是唯一的。如使用同一ID发送两条消息,则第二条消息会替换掉第一条消息。* 这也是进度条或其他动态视觉效果的实现方式。*/notificationManager.notify(requestCode, notification);}}

登记新建的receiver。既然该receiver负责发送通知消息,并接收其他接受者返回的结果码,它的运行应该在最后。这就需要将receiver的优先级设置为最低。为保证receiver最后运行,设置其优先级值为-999(-1000及以下值属系统保留值)。

另外,既然NotificationReceiver仅限于应用内部使用,我们还需设置属性值为android:exported=”false”,以保证其对外部应用不可见。

  <receiver
            android:name="com.example.photogallery.NotificationReceiver"android:exported="false" ><intent-filter android:priority="-999" ><action android:name="com.example.photogallery.SHOW_NOTIFICATION" /></intent-filter></receiver>

receiver 与长时运行任务

如不想受限于主线程的时间限制,并希望broadcast intent可触发一个长时运行任务,该怎么做呢?

有两种方式可以选择:
1、将任务交给服务去处理,然后通过broadcast receiver启动服务。这也是推荐的方式。服务可以运行很久,直到完成需要处理的任务。同时服务可将请求放在队列中,然后依次处理,或按其自认为合适的方式管理全部任务请求。
2、使用BroadcastReceiver.goAsync()方法。该方法返回一个BroadcastReceiver.PendingResult对象,随后,我们可使用该对象提供结果。因此,可将PendingResult交给AsyncTask去执行长时运行的任务,然后再调用PendingResult的方法响应broadcast。

BroadcastReceiver.goAsync()方法有两处弊端。首先它不支持旧设备。其次,它不够灵活:我们仍需快速响应broadcast,并且与使用服务相比,没什么架构模式好选择。

当然,goAsync()方法并非一无是处:可通过该方法的调用,完成有序broadcast的结果设置。如果真的要使用它,应注意不要耗时过长。

代码地址

这篇关于广播接受者(Broadcast Receiver)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/943100

相关文章

android面试:如何理解 Android 中的广播?

在 Android 中,广播是一种用于在应用程序之间传递消息的机制。它允许应用程序发送和接收全局消息,通常用于通知其他应用或组件某些事件的发生。广播可以是系统广播(由系统发送)或自定义广播(由应用程序发送)。 广播的主要特点: 异步通信:广播是一种异步的通信方式,发送广播的应用不需要等待接收应用的响应。 全局可见:广播可以被系统中的任何应用程序接收,适合用于跨应用的消息传递。 轻量级:广播

Android 接收系统广播,开机启动Service,SMS,Battery

本文内容摘自《疯狂Android讲义 第3版》李刚 著 自动开启的Service: package shortcut.song.com.myapplication;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;im

Android Ordered Broadcast 有序广播

代码设置IntentFilter: IntentFilter intentFilter = new IntentFilter();intentFilter.setPriority(15);intentFilter.addAction("shortcut.song.com.myapplication.MY_BROADCAST");intentFilter.addCategory

Android BroadcastRecevier广播消息

代码注册Receiver @Overrideprotected void onCreate(Bundle savedInstanceState) {IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("");MyBroadcast myBroadcast = new MyBroadcast();regi

Flink使用Broadcast State实现流处理配置实时更新

大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 本文作者时延军发表在http://shiyanjun.cn,如果你也在使用Broadcast State,那么可以参考一下。 Broadcast State是Flink支持的一种Operator State。使用Broadcast State,可以在Flink程序的一个Stream中输入数

2024全球广播摄像机市场前景如何?IP网络化云端融合趋势加强

一、前言 当前,全球广播电视用户持续向流媒体迁移。大多数国家广播电视台推出独立流媒体平台,流媒体趋势日渐加强。与奈飞等非广电系流媒体平台只提供点播服务不同,广电系流媒体平台一般提供电视频道直播服务、电视回看服务、视频点播服务,与传统电视渠道形成互补,而这些流媒体平台与网络媒体平台所能提供的视频服务存在着非常重合的区域。 (1)流媒体时代加速广播摄像机专业影视化发展 全球广电系流媒体平台竞争力

单播、广播、多播

单播、多播和广播单播”(Unicast)、“多播”(Multicast)和“广播”(Broadcast)这三个术语都是用来描述网络节点之间通讯方式的术语。那么这些术语究竟是什么意思?区别何在? 1.单播:网络节点之间的通信就好像是人们之间的对话一样。如果一个人对另外一个人说话,那么用网络技术的术语来描述就是“单播”,此时信息的接收和传递只在两个节点之间进行。单播在网络中得到了广泛的应用,网络上

RocketMQ广播消费消息

1、 基础概念 RocketMQ 支持两种消息模式:集群消费( Clustering )和广播消费( Broadcasting )。 集群消费模式(Cluster): 在集群消费模式下,同一个消费者组(Consumer Group)中的每个消费者都会消费消息的一个副本。消息会被分发到不同的消费者实例上,但是同一个消息只会被同一个消费者组中的一个消费者消费。 广播消费模式(Broadcast)

IOS消息分发(广播)机制

在IOS中,提供了通知机制(Notification),可以在对象间传递和接受信息。传递和接受信息的对象间甚至不需要知道对方的存在。究其本质来说,其实是设计模式中的观察者模式的应用。 通知机制 设想这么一个场景:我开发了一款pdf阅读器,当手机上的另一个App打开pdf文件时,通过Open in,选择我的pdf阅读器打开。这时候我的pdf阅读器会被lanuch,同时在其App deleg

神奇的android广播

最近用了android的广播,个人感觉非常好用: 首先在你要接收的地方注册一个: context.registerReceiver(myReceiver, new IntentFilter("com.shic.action.d")); 然后就是定义注册的这个,在接收到广播后执行的操作: BroadcastReceiver myReceiver = new BroadcastRecei