Android查缺补漏--BroadcastReceiver的类型与使用

Broadcast 是一种被用于应用内和应用之间传递信息的机制。一个广播可以对应多个接受者。一个完整的广播机制,需要具有以下三个要素:

  • 发送广播的Broadcast
  • 接受广播的BroadcastReceiver
  • 传递信息的Intent

广播的注册分为静态注册和动态注册:

  • 静态注册:静态注册的广播是指在AndroidManifest中注册的广播,此种广播在应用安装时就被系统解析,不需要启动应用就可以收到相应的广播。
<receiver android:name=".broadcast.MyBroadcastReceiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>
  • 动态注册:通过Context.registerReceiver()来实现,不需要时要通过Context.unRegisterReceiver()解除广播,此种广播必须应用启动后才能注册并接收广播。 // 动态注册广播接收器 registerReceiver(new DynamicBroadcastReceiver(), new IntentFilter(MyBroadcastReceiver.ACTION));

广播又分为普通广播、有序广播、本地广播和sticky广播。

一、普通广播

普通广播通过Context.sendBroadcast()发送,我们没有办法制定Receiver们对于普通广播的接收顺序。理论上所有的接收器(Receiver)接收到广播的顺序不确定,但一般是按照其在AndroidMainfest.xml文件中注册的顺序(不绝对)。 普通广播中,接受者不能将处理结果传递给下一个接收器,也无法终止广播的传播。

如下代码是一个静态注册的广播示例:

public class MyBroadcastReceiver extends BroadcastReceiver {

    String TAG = MyBroadcastReceiver.class.getSimpleName();

    public static final String ACTION = "MY_BROADCAST_RECEIVER";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "接收到广播消息:" + intent.getStringExtra(BroadcastTestActivity.INTENT_INFO));
    }
}

然后再AndroidMainfest.xml中注册这个广播:

<receiver android:name=".broadcast.MyBroadcastReceiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

接下来在Activity中调用Context.sendBroadcast()发送广播就可以了:

Intent intent = new Intent(MyBroadcastReceiver.ACTION);
intent.putExtra(INTENT_INFO, "我是一个普通广播");
sendBroadcast(intent);

log如下:

12-08 17:29:44.259 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcastReceiver: 接收到广播消息:我是一个普通广播
  • 普通广播的接收顺序测试 我们仿照MyBroadcastReciver创建多个接收器,代码一样:
/**
 * 静态注册的广播接收器2
 * Created by liuwei on 17/12/7.
 */
public class MyBroadcast2Receiver extends BroadcastReceiver {

    String TAG = MyBroadcast2Receiver.class.getSimpleName();

    public static final String ACTION = "MY_BROADCAST_RECEIVER";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "接收到广播消息:" + intent.getStringExtra(BroadcastTestActivity.INTENT_INFO));
    }
}
/**
 * 静态注册的广播接收器3
 * Created by liuwei on 17/12/7.
 */
public class MyBroadcast3Receiver extends BroadcastReceiver {...}
/**
 * 静态注册的广播接收器4
 * Created by liuwei on 17/12/7.
 */
public class MyBroadcast4Receiver extends BroadcastReceiver {...}
/**
 * 静态注册的广播接收器5
 * Created by liuwei on 17/12/7.
 */
public class MyBroadcast5Receiver extends BroadcastReceiver {...}
/**
 * 静态注册的广播接收器6
 * Created by liuwei on 17/12/7.
 */
public class MyBroadcast6Receiver extends BroadcastReceiver {...}

然后在AndroidMainfest.xml中为以上广播都注册同一个action

<receiver android:name=".broadcast.MyBroadcastReceiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast6Receiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast2Receiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast3Receiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast4Receiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast5Receiver">
    <intent-filter>
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

点击发送广播,查看log:

12-08 17:29:44.259 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcastReceiver: 接收到广播消息:我是一个普通广播
12-08 17:29:44.268 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcast6Receiver: 接收到广播消息:我是一个普通广播
12-08 17:29:44.271 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcast2Receiver: 接收到广播消息:我是一个普通广播
12-08 17:29:44.273 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcast3Receiver: 接收到广播消息:我是一个普通广播
12-08 17:29:44.277 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcast4Receiver: 接收到广播消息:我是一个普通广播
12-08 17:29:44.280 6644-6644/cn.codingblock.androidadvancestudy I/MyBroadcast5Receiver: 接收到广播消息:我是一个普通广播

二、有序广播

在AndroidMainfest.xml中注册广播时通过priority(值越优先级越高)节点为广播添加优先级,然后再用Context.sendOrderedBroadcast()发送,接收者们就会按照优先级顺序依次执行。

有序广播的接收者和给下一个接收者传递数据,并且接收者在收到广播之后可以抛弃广播,使广播不再向后传递。

为上面6个接收器添加优先级:

<receiver android:name=".broadcast.MyBroadcastReceiver">
    <intent-filter android:priority="1">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast6Receiver">
    <intent-filter android:priority="6">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast2Receiver">
    <intent-filter android:priority="2">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast3Receiver">
    <intent-filter android:priority="3">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast4Receiver">
    <intent-filter android:priority="4">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

<receiver android:name=".broadcast.MyBroadcast5Receiver">
    <intent-filter android:priority="5">
        <action android:name="MY_BROADCAST_RECEIVER" />
    </intent-filter>
</receiver>

然后通过sendOrderedBroadcast发送广播观察log:

intent = new Intent(MyBroadcastReceiver.ACTION);
intent.putExtra(INTENT_INFO, "我是一个有序广播");
sendOrderedBroadcast(intent, null);

log如下:

12-08 18:17:26.455 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcast6Receiver: 接收到广播消息:我是一个有序广播
12-08 18:17:26.462 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcast5Receiver: 接收到广播消息:我是一个有序广播
12-08 18:17:26.464 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcast4Receiver: 接收到广播消息:我是一个有序广播
12-08 18:17:26.465 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcast3Receiver: 接收到广播消息:我是一个有序广播
12-08 18:17:26.466 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcast2Receiver: 接收到广播消息:我是一个有序广播
12-08 18:17:26.467 25919-25919/cn.codingblock.androidadvancestudy I/MyBroadcastReceiver: 接收到广播消息:我是一个有序广播
  • abortBroadcast()抛弃广播: 普通的广播是没有办法抛弃的,否则会抛出RuntimeException的异常。

只有有序广播才可以通过此方法抛弃。我们在MyBroadcast6Receiver中添加abortBroadcast()方法:

public class MyBroadcast6Receiver extends BroadcastReceiver {

    String TAG = MyBroadcast6Receiver.class.getSimpleName();

    public static final String ACTION = "MY_BROADCAST_RECEIVER";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "接收到广播消息:" + intent.getStringExtra(BroadcastTestActivity.INTENT_INFO));
        abortBroadcast();
        Log.i(TAG, "丢弃广播");
    }
}

然后点击发送有序广播,log如下:

12-08 18:34:27.989 1329-1329/cn.codingblock.androidadvancestudy I/MyBroadcast6Receiver: 接收到广播消息:我是一个有序广播
12-08 18:34:27.989 1329-1329/cn.codingblock.androidadvancestudy I/MyBroadcast6Receiver: 丢弃广播

可以看到广播已经被丢弃了。

  • setResult()传递给下一个接收者结果。
  • getResult()接收上一个接收者的结果。

在MyBroadcast6Receiver中添加setResult方法,在MyBroadcast5Receiver添加getResult方法:

public class MyBroadcast6Receiver extends BroadcastReceiver {

    String TAG = MyBroadcast6Receiver.class.getSimpleName();

    public static final String ACTION = "MY_BROADCAST_RECEIVER";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "接收到广播消息:" + intent.getStringExtra(BroadcastTestActivity.INTENT_INFO));
//        abortBroadcast();
//        Log.i(TAG, "丢弃广播");
        setResult(006, "我是老6传来的消息", null);
    }
}
public class MyBroadcast5Receiver extends BroadcastReceiver {

    String TAG = MyBroadcast5Receiver.class.getSimpleName();

    public static final String ACTION = "MY_BROADCAST_RECEIVER";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "接收到广播消息:" + intent.getStringExtra(BroadcastTestActivity.INTENT_INFO));
        String data = getResultData();
        Log.i(TAG, "data=" + data);
    }
}

log如下:

12-08 18:40:01.415 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast6Receiver: 接收到广播消息:我是一个有序广播
12-08 18:40:01.434 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast5Receiver: 接收到广播消息:我是一个有序广播
12-08 18:40:01.434 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast5Receiver: data=我是老6传来的消息
12-08 18:40:01.440 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast4Receiver: 接收到广播消息:我是一个有序广播
12-08 18:40:01.442 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast3Receiver: 接收到广播消息:我是一个有序广播
12-08 18:40:01.445 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcast2Receiver: 接收到广播消息:我是一个有序广播
12-08 18:40:01.447 10372-10372/cn.codingblock.androidadvancestudy I/MyBroadcastReceiver: 接收到广播消息:我是一个有序广播

三、本地广播

以上的广播对于系统来说是全局的,发出广播后,系统内的应用只要注册了相应的接收器就都可以收到广播。如果我们想在本应用发出的广播只在此应用内可以收到,那么可以使用本地广播了。

本地广播由 LocalBroadcastManager 管理,是在 API 21 以后添加的,使用起来也很方便,需要先通过 LocalBroadcastManager.getInstance() 方法获取其单例,剩下的用法与其他广播类似,其主要方法有以下几个:

  • registerReceiver():注册广播接收器。
  • unregisterReceiver():解除广播接收器。
  • sendBroadcast():发送异步广播。
  • sendBroadcastSync():发送同步广播。

使用本地广播时,无需在AndroidMainfest.xml中注册,必须使用 LocalBroadcastManager.getInstance(...).registerReceiver(..)来注册接收器。

我们来写个本地广播的小栗子,首先注册两个本地广播:

LocalBroadcastManager.getInstance(context).registerReceiver(new MyBroadcastReceiver(), new IntentFilter(MyBroadcastReceiver.ACTION));
LocalBroadcastManager.getInstance(context).registerReceiver(new MyBroadcast2Receiver(), new IntentFilter(MyBroadcastReceiver.ACTION));

然后发送本地广播:

intent.putExtra(INTENT_INFO, "我是一个本地广播");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

log如下:

12-09 17:20:47.799 15019-15019/cn.codingblock.androidadvancestudy I/MyBroadcastReceiver: 接收到广播消息:我是一个本地广播
12-09 17:20:47.799 15019-15019/cn.codingblock.androidadvancestudy I/MyBroadcast2Receiver: 接收到广播消息:我是一个本地广播

四、sticky广播(不建议使用)

sticky广播会一直处于滞留状态,sticky广播被发出后,只要有能够匹配其的新接收器被注册了就可以收到广播,sticky广播通过Context.sendStickyBroadcast()发送。

最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!

参考文献:

  • 《Android开发艺术探索》
  • 《Android开发进阶从小工到专家》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏24K纯开源

Ubuntu13.04配置:Vim+Syntastic+Vundle+YouCompleteMe

序言     使用Ubuntu和vim已经有一段时间了,对于Vim下的插件应用,我总是抱着一股狂热的态度。这次,又在网上闲逛着,发现了一个个人博客提到了Vim代...

25210
来自专栏Android-薛之涛

Activity启动模式(launchMode)详解

      好了,今天想整理一下Activity启动模式的笔记,毕竟项目开发中有涉及且之前面试都有问道,抽周末做个总结。

952
来自专栏数据和云

Oracle 12c多租户特性详解:全局用户与本地用户的原理与维护

? 前情回顾:Oracle 12c多租户特性详解:从Schema到PDB的变化与隔离 COMMON 和 Local 用户 无论在 CDB 和 Non-CDB...

2845
来自专栏向治洪

React Native移植原生Android

(一)前言 之前已经写过了有关React Native移植原生Android项目的文章,不过因为RN版本更新的原因吧,跟着以前的文章可能会出现一些问题,对于初学...

1847
来自专栏运维

Jewel版本Ceph集群功能性能测试

http://docs.ceph.com/docs/master/start/quick-start-preflight/#rhel-centos https:...

672
来自专栏乐沙弥的世界

使用nid命令修改 db name 及 dbid

    工作中不可避免地碰到需要修改dbname以及dbid的情形,如将数据库恢复到同一台机器的情形是其中之一。但dbname以及dbid是用于标识数据库的重要...

772
来自专栏数据之美

linux 系统监控、诊断工具之 lsof 用法简介

1、lsof 简介 lsof 是 linux 下的一个非常实用的系统级的监控、诊断工具。 它的意思是 List Open Files,很容易你就记住了它是 “...

3466
来自专栏北京马哥教育

Linux 命令神器:lsof 入门

lsof是系统管理/安全的尤伯工具。我大多数时候用它来从系统获得与网络连接相关的信息,但那只是这个强大而又鲜为人知的应用的第一步。将这个工具称之为lsof真实名...

2777
来自专栏吴小龙同學

Android之BroadcastReceiver

什么是BroadcastReceiver?   BroadcastReceiver,广播接收者,它是一个系统全局的监听器,用于监听系统全局的Broadcast消...

3485
来自专栏数据和云

New in 12.2- 让每个PDB都拥有自己的 local undo表空间

12.1引入temp undo概念 ,12.2引入local undo,也就是每个pdb拥有自己的undo表空间(以前版本共用一个undo表空间),rac中每个...

3555

扫码关注云+社区