Android笔记—Broadcast广播


资料来源如下

  • 第一行代码(第二版)

编程环境

  • Android Studio 2.2.3

BroadcastReceiver基础

  • 首先祭出官方文档及中文翻译

    https://developer.android.com/reference/android/content/BroadcastReceiver.html
    http://www.jianshu.com/p/1b56172b0c77

BroadcastReceiver概述

  1. @别路寻忆
    Android中的四大组件是 Activity、Service、Broadcast和Content Provider。而Intent是一个对动作和行为的抽象描述,负责组件之间程序之间进行消息传递。那么Broadcast Receiver组件就提供了一种把Intent作为一个消息广播出去,由所有对其感兴趣的程序对其作出反应的机制。

  2. @zuolongsnail专栏

    • 广播接收器是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统代码的──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。

    • 应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。

    • 广播接收器没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

广播分类

  • 触发广播发生的事件分类有两种

    1. Android系统广播事件
      由android设备状态变化而触发的系统广播如:
      Intent.ACTION_POWER_CONNECTED;
      //插上外部电源时发出的广播
      Intent.ACTION_SCREEN_ON;
      //屏幕被打开之后的广播
      ACTION_TIME_CHANGED
      //系统时间改变而触发的广播
    2. 自定义的广播事件
      这个比较好理解了,比如qq当我们再另外一台手机上登陆时,手头的这个手机qq就会自动下线。腾讯的服务器通过后台服务启用了自定义广播来终结正在运行的Activity,详情之后会有例程,不再累赘。
  • 能够被接收的广播类型

    1. 普通广播(Normal broadcasts):
      (由 Context.sendBroadcast发出)异步发出。所有广播接收器都可以在同一时间接收广播。广播接收者无法接收广播的处理结果或者阻断广播的传递。

    2. 有序广播(Ordered broadcasts):
      (由 Context.sendOrderedBroadcast发出)每次只发送给一个广播接收器。当每个广播接收器依次执行时,它可以向下一个广播接收器传播结果,或者阻断该广播,使得该广播不能被下一个广播接收器接收到。

    • 通俗解释:
      普通广播就相当于小区/村委会的大喇叭,有事发生(触发广播)通知所有的人(广播接收器),全功率的大喊大叫确保所有人(广播接收器)都能听到。而有人(广播接收器)在睡觉被吵醒但是又没办法砸了那个大喇叭,只能继续听着(无法接收广播的处理结果或者阻断广播的传递)。
      有序广播 就相当于间谍机关的绝密消息传递。绝密到手(触发广播)间谍秘密汇总给上线A(广播接收器A)。本来 上线(广播接收器A)应该 听取完毕重新整理情向首长B(广播接收器B)报告(向广播接收器B传播 广播接收器A修改的结果),但是A被收买,将情报隐匿了,没有向首长B汇报(广播接收器A阻断该广播,使得该广播不能被下一个广播接收器B接收到)。(PS结局首长B错误带人炸了村委会,该睡觉的人终于可以安生睡觉了!)
  • 广播事件注册有两种

    1. 静态注册,就是在AndroidManifest.xml文件中定义,注册的广播接收器必须要继承BroadcastReceiver.
    2. 动态注册,是在程序中使用Context.registerReceiver注册,注册的广播接收器相当于一个匿名类。两种方式都需要IntentFIlter。
    • 例程见下节。

动态注册(在代码中注册)

  • 在代码中通过registerReceiver()注册。app关闭后,该接收器也会随之销毁。

  • 首先定义一个内部子类NetworkChangeReceiver继承自 BroadcastReceiver

    1
    2
    3
    4
    5
    6
    7
    8
    class NetworkChangeReceiver extends BroadcastReceiver {
    @Override

    //重写onReceive,接收到广播后提示消息
    public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "network is available网络已变化", Toast.LENGTH_SHORT).show();
    }
    }
  • 在onCreate()方法中创建IntentFilter实例和NetworkChangeReceiver实例。并在IntentFilter实例中添加网络变化时系统广播对应值。随后传入registerReceiver()中注册。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //创建IntentFilter实例
    intentFilter = new IntentFilter();
    //添加对应系统广播
    intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
    //添加NetworkChangeReceiver实例
    networkChangeReceiver = new NetworkChangeReceiver();
    //动态注册
    registerReceiver(networkChangeReceiver, intentFilter);
    }
  • 动态注册的广播接收器最后要取消注册。在onDestroy()方法中调用unregisterReceiver()销毁动态注册的广播接收器。

    1
    2
    3
    4
     protected void onDestroy(){
    super.onDestroy();
    unregisterReceiver(networkChangeReceiver);
    }
  • 最后要在AndroidMainfest.xml中声明查询系统网络状态的权限。

    1
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  • 运行效果如如所示(模拟器)

静态注册(xml中注册)

  • 直接在Manifest.xml文件中配置广播接收者

  • 例程为了方便同样以android.net.conn.CONNECTIVITY_CHANGE为例,与动态注册相同。

    不使用内部子类(第一行代码)

  • 新建名称为BootCompleteReceiver的java class 代码如下

    1
    2
    3
    4
    5
    6
       public  class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
    Toast.makeText(context, "网络改变", Toast.LENGTH_SHORT).show();
    }
    }
  • 在AndroidManifest.xml文件中注册广播接收器
    代码如下

    1
    2
    3
    4
    5
    <receiver android:name=".BootCompleteReceiver">
    <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
    </intent-filter>
    </receiver>
  • 在AndroidManifest.xml中声明权限

    1
    2
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>

    使用内部子类(在Activity中定义)


在这晕圈了一下午,找不出毛病。看书多仔细吧,书上说了不用内部子类。
详细资料在这里

http://blog.csdn.net/chdjj/article/details/19496567


  • 清单文件注册广播接收者时,广播接收者的名字格式需要注意因为是内部类,所以需要在内部类所在的类与内部类之间加上$符号(这一点在AndroidStudio中输入时有提示)
  • 内部类在声明时一定要写成静态内部类(class关键字前加上static)。否则会抛出异常(广播发生时,应用停止运行)

  • 在MainActivity中新建子类

    1
    2
    3
    4
    5
    6
     public static class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
    Toast.makeText(context, "Boot complete", Toast.LENGTH_SHORT).show();
    }
    }
  • 在AndroidManifest.xml中注册广播接收器

    1
    2
    3
    4
    5
    <receiver android:name=".MainActivity$BootCompleteReceiver">
    <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
    </intent-filter>
    </receiver>
  • 在AndroidManifest.xml中声明权限

    1
    2
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
  • 运行效果同动态注册

使用广播接收器注意事项

  • 在onReceive()方法中不宜添加过多逻辑/耗时操作,广播接收器没有多线程,一旦时间过长,程序就会报错。
  • 广播接收器一般为启动其他组件作用。

发送标准广播


  • 发送标准广播之前,首先要注册一个作为目标的广播接收器。(过程略,只上代码)
    新建MyBroadcastReceiver.class
    1
    2
    3
    4
    5
    6
    7
       public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent)
    {
    Toast.makeText(context,"MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
    }
    }
    在Xml中注册(静态)
    1
    2
    3
    4
    5
    <receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
    <action android:name="com.example.broadcasttest.MyBroad"/>
    </intent-filter>
    </receiver>

  • xml中注册一个Button

    1
    2
    3
    4
    5
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="SendBroad"
    android:text="SendBroad" />
  • MainActivity中新建 SendBroad()函数
    首先构建一个 Intent对象,将自定义的广播值填入。再调用sendBroadcast方法将广播发送出去。

    1
    2
    3
    4
    5
        public void SendBroad(View view)
    {
    Intent intent = new Intent("com.example.broadcasttest.MyBroad");
    sendBroadcast(intent);
    }
  • 发送的是标准广播。运行效果如下图。
    Screenshot_20160910-220121.png

发送有序广播


  • 首先新建一个Broad2 的工程。同样接收Broad发送的广播。
    代码如下
    1
    2
    3
    4
    5
    6
    7
       public class AnotherBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent)
    {
    Toast.makeText(context," AnotherBroadcast",Toast.LENGTH_SHORT).show();
    }
    }
    1
    2
    3
    4
    5
    <receiver android:name=".AnotherBroadcast">
    <intent-filter android:priority="10">
    <action android:name="com.example.broadcasttest.MyBroad"/>
    </intent-filter>
    </receiver>
  • 测试效果:当摁下发送广播按钮后,弹出两个提示。

  • 修改Broad项目中onClick对应事件。将sendBroadcast()改为sendOrderedBroadcast();发送有序广播。

    1
    2
    3
    4
    5
        public void SendBroad(View view)
    {
    Intent intent = new Intent("com.example.broadcasttest.MyBroad");
    sendOrderedBroadcast(intent,null);
    }
  • 效果与发送标准广播相同(还未定义优先级/截断等)

  • 定义优先级,再Broad的AndroidMainfest.xml中修改注册的广播添加android:priority=”100”优先级100

    1
    2
    3
    4
    5
    <receiver android:name=".MyBroadcastReceiver">
    <intent-filter android:priority="100">
    <action android:name="com.example.broadcasttest.MyBroad"/>
    </intent-filter>
    </receiver>
  • 在Broad2的AndroidMainfest.xml中添加android:priority=”10”优先级10

    1
    2
    3
    4
    5
    <receiver android:name=".AnotherBroadcast">
    <intent-filter android:priority="10">
    <action android:name="com.example.broadcasttest.MyBroad"/>
    </intent-filter>
    </receiver>
  • 发送广播后MyBroadcastReceiver最先收到广播。

  • 截断广播。有序广播中前一个广播接收器可以截断广播传播。添加 abortBroadcast();即可。

    1
    2
    3
    4
    5
    6
    7
    8
       public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent)
    {
    Toast.makeText(context,"MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
    abortBroadcast();
    }
    }
  • 再次点击按钮发送广播,只有MyBroadcastReceiver可以接收到发送的广播。

本地广播

  • 只在app应用内部传递的广播。注册过程类似于动态注册。

  • 定义一个内部类LocalReceiver继承自BroadcastReceiver

    1
    2
    3
    4
    5
    6
        public static class LocalReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent){
    Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();
    }
    }
  • 首先在onCreate()方法中通过localBroadcastManager.getInstan得到一个LocalBroadcastManager的实例,再创建IntentFilter实例和LocalReceiver实例。并在IntentFilter实例中添加广播。随后传入localBroadcastManager.registerReceiver()中注册本地广播。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

    localBroadcastManager = localBroadcastManager.getInstance(this);

    intentFilter = new IntentFilter();
    intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
    localReceiver = new LocalReceiver();
    localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }
  • 在Activity的onDestroy()中销毁注册。

    1
    2
    3
    4
    5
    @Override
    protected void onDestroy(){
    super.onDestroy();
    localBroadcastManager.unregisterReceiver(localReceiver);
    }
  • 在Button对应的函数中调用localBroadcastManager.sendBroadcast发送本地广播。

    1
    2
    3
    4
    5
        public void SendBroad(View view)
    {
    Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
    localBroadcastManager.sendBroadcast(intent);
    }
  • 效果基本同上,不加累赘。不过在Broad2中是怎样都搜不到广播了。

  • 本地广播特点

    • 明确广播只在应用内部,传递数据无需担心泄密。
    • 其他程序广播无法发送至程序内部。
    • 本地广播比全局广播更为高效。