一、了解

1. 定义

    BroadcastReceiver(广播接收器)即广播,是一个全局的监听器。

2. 意义

     用于响应来自应用APP或者系统的广播消息

3. 应用场景

  1. 同一 App 内部的同一组件内的消息通信(单个或多个线程之间);
  2. 同一 App 内部的不同组件之间的消息通信(单个进程);
  3. 同一 App 具有多个进程的不同组件之间的消息通信;
  4. 不同 App 之间的组件之间消息通信;
  5. Android系统在特定情况下与App之间的消息通信,如:网络变化、电池电量、屏幕开关等

二、注册

    首先创建一个类,继承自BroadcastReceiver,并重写onReceiver()方法。onReceiver()方法中的内容就是当广播接收器接收到广播时要处理的事务。这里就是简单的发出一条toast。

public class BroadcastReceiverTest extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "这是一条广播~", Toast.LENGTH_SHORT).show();
    }
}

    注册分为两种注册方式,一种为静态注册,一种为动态注册, 下面将会分别讲解两种注册方式,并会对两种注册方式进行一个对比。

1. 静态注册

    提示:Android 8.0新特性-取消大部分静态注册广播

    静态注册需要在AndroidManifest.xml清单文件中进行注册

  1. 在AndroidManifest中的application标签下加上receiver子标签
  2. 通过"android: name= "注册一个广播类(其中name后需写出你要注册的文件路径)
  3. android:enabled="true" 代表是否允许该广播接收器接受本程序以外的广播
  4. android:exported="true" 代表是否启用这个广播接收器
  5. 在receiver下加上intent-filter标签,设置其action,里面的内容可以是系统定义的系统广播的“频道”,也可以是自定义的广播的“频道”
<receiver
    android:name="com.example.mytest.BroadcastReceiverTest"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
         <action android:name="android.intent.action.REBOOT" /> <!-- 设备重启广播 -->
    </intent-filter>
</receiver>

    想监听什么广播,就添加相应的action标签。(注意:一些系统广播是需要声明权限的)

    (intent-filter标签可以理解为过滤器,当系统中出现与过滤器中标签相符的广播时,便代表接收到了广播,开始执行广播接收器中的onReceiver()方法)

    如果你想监听多条广播也是可以的,添加多个intent-filter标签即可

<receiver
    android:name="com.example.mytest.BroadcastReceiverTest"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_ACL_CONNECTED" /> <!-- 设备重启 -->
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" /> <!-- 在系统完成启动后广播一次 -->
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.DATE_CHANGED" /> <!-- 日期发生改变 -->
    </intent-filter>
</receiver>

些人问 Android 8.0不是取消大部分静态注册广播了吗?没关系,后文有解决的办法,请继续向下看。

2.动态注册 

    动态注册只需要在java文件中进行注册即可

  1. 创建一个广播接收器类的对象 "broadcastReceiverTest"
  2. 创建一个IntentFilter类的对象 "intentFilter"
  3. 调用intentFilter的.addAction()方法存入广播“频道”
  4. 最后,调用context.registerReceiver(broadcastReceiverTest, intentFilter)方法注册,其中,第一个参数为广播接收器的对象,第二个参数为IntentFilter类的对象
IntentFilter intentFilter = new IntentFilter();
BroadcastReceiverTest broadcastRecevierTest = new BroadcastReceiverTest();
intentFilter.addAction("com.example.mytest.BroadcastReceiverTest"); //这是一条自定义的广播
registerReceiver(broadcastRecevierTest, intentFilter);

    这样一条动态注册就注册成功了,注册成功后还需要记得,动态注册的广播接收器一定要取消注册才行,否则可能会引起内存泄漏。

    一般可以选择写在onDestroy()方法中,当然,如果你担心Activity没有执行到onDestroy()方法,你也可以写在onStop()方法中。

protected void onDestroy() {
      super.onDestroy();
      unregisterReceiver(broadcastReceiverTest);
}

3.注册方式对比

  1. 静态注册依附于清单文件,只要APP启动过一次,所静态注册的广播就会生效,无论当前的APP处于停止使用还是正在使用状态。只要相应的广播事件发生,系统就会遍历所有的清单文件,通知相应的广播接收者接收广播,然后调用广播接收者的onReceiver方法。
  2. 动态注册方式依赖于所注册的组件,当APP关闭后,组件对象都不在了动态注册的代码都不存在了,所动态注册监听的action自然不在生效。
  3. 静态注册的广播传播速度要远远慢于动态注册的广播。

4.提示 

    到目前为止,我们在广播接收器的onReceive()方法中都只是简单的用Toast提示了一段文本信息,当你在项目中时可以编写自己的逻辑,但要注意的是,不要在onReceive()方法中添加过多的逻辑或者进行任何耗时的操作,因为广播接收器是不允许开启线程的,当onReceive()方法运行了较长时间没有结束时,程序就会报错。

三、发送广播

    发送广播分为三种方式,分别为标准(无序)广播、有序广播、本地广播。

1. 发送标准(无序)广播

    标准广播是所有与之匹配的广播接收者都能收到的广播,没有先后顺序,直到没有广播接收者接收广播为止才会停止广播的传递。

  1. 首先创建一个Intent对象,把广播的“频道”值传入
  2. 然后调用context的sendBroadcast()方法将广播发送出去,参数为刚才创建的Intent对象
  3. 之后, 所有监听“com.example.mytest.BroadcastReceiverTest”这条频道的广播接收器就会收到该广播
Intent intent = new Intent("com.example.mytest.BroadcastReceiverTest");
sendBroadcast(intent);

 注意:由于广播是使用Intent传递的,因此可以在Intent中携带一些数据进行传递。

2. 发送有序广播 

    有序广播是一种分先后广播接收器的广播,广播接收者的优先级越高,越先接收广播。优先级高的广播先收到广播,收到广播后可以修改广播的内容,也可以拦截广播不让广播向下传递。

    有序广播和无序广播步骤相同,不同点便是将sendBroadcast()方法换成sendOrderedBroadcast()方法

  1. 首先创建一个Intent对象,把广播的“频道”值传入
  2. 然后调用context的sendOrderedBroadcast()方法将广播发送出去,第一个参数为刚才创建的Intent对象,第二个参数是与权限相关的字符串,一般不用时填写null即可
  3. 之后, 所有监听“com.example.mytest.BroadcastReceiverTest”这条频道的广播接收器就会收到该广播
Intent intent = new Intent("com.example.mytest.BroadcastReceiverTest");
sendOrderedBroadcast(intent,null);

    由于有序广播是分优先级的,那么如何设置优先级呢?

    第一种方法是静态注册时在Intent-filter标签中设置

<receiver
      android:name="com.example.mytest.BroadcastReceiverTest"
      android:enabled="true"
      android:exported="true">
      <intent-filter android:priority="100">
          <action android:name="com.example.mytest.BroadcastReceiverTest" /> <!-- 设备重启 -->
      </intent-filter>
</receiver>

    或者在动态注册时调用IntentFilter类对象的setPriority()方法来设置

IntentFilter intentFilter = new IntentFilter();
BroadcastRecevierTest broadcastRecevierTest = new BroadcastReceiverTest();
intentFilter.addAction("com.example.mytest.BroadcastReceiverTest"); //这是一条自定义的广播
intentFilter.setPriority(100); //设置广播的优先级
registerReceiver(broadcastRecevierTest, intentFilter);

    广播优先级取值的范围为-1000~10000,数值越大优先级越高,优先级高的广播可以先收到并选择截断广播,截断广播只需要在你重写的onReceive()方法中加入abortBroadcast()方法即可,后面的广播接收器将不再接受此广播,你也可以存入新的数据向后继续传递。

3. 本地广播

    前面我们的发送与接收都属于系统全局广播,即发出的广播可以被任何其他APP收到,我们也可以接收到任何其他APP的广播,这样容易引起安全问题。

    为了能简单的解决安全问题,Android引入了本地广播,本地广播是只在APP内部进行传递的一种广播方式,它的好处在于不会引起安全问题,不用担心数据的泄露。

    本地广播和标准广播步骤基本一样,只是使用了一个LocalBroadcastManager(需要添加依赖)来对广播进行管理,相比标准广播只是每一个步骤多添加了一段代码。

  1. 本地广播的创建首先先引入一个LocalBroadcastManager类
  2. 然后通过LocalBroadcastManager的getInstance()方法获取他的一个实例
  3. 然后注册时调用的是LocalBroadcastManager类对象的registerReceiver()方法
  4. 发送时调用的是LocalBroadcastManager类对象的sendBroadcast()方法
  5. 取消注册时调用的也是LocalBroadcastManager类对象的unregisterReceiver()方法
public class LocalBroadcastRecevierTest extends AppCompatActivity {

    private Button bt_am_baroadcastreciver;

    private IntentFilter intentFilter;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例
        bt_am_baroadcastreciver = findViewById(R.id.bt_am_baroadcastreciver);
        bt_am_baroadcastreciver.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.mytest.BroadcastReceiverTest");
                localBroadcastManager.sendBroadcast(intent); //发送本地广播
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.mytest.BroadcastReceiverTest");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter); //注册本地广播监听器
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver); //取消注册
    }
}

class LocalReceiver extends BroadcastRecevierTest{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "这是一条本地广播~", Toast.LENGTH_SHORT).show();
    }
}

注意:本地广播是无法通过静态注册来注册的,因为他的目的只是在APP内部传递消息,也就用不到静态注册

    本地广播的优势:

  1. 广播不会离开程序,不用担心泄露数据。
  2. 其他程序无法将广播发送到我们程序内部,不用担心安全漏洞隐患。
  3. 本地广播比系统全局广播更加高效。 

四、Android8.0后的静态注册

    Android 8.0以上可以使用setComponent()来指定包名和类名 。

    在注册时调用Intent类对象的setComponent()方法。

Intent intent = new Intent("com.example.mytest.BroadcastReceiverTest");
//使用setComponent()方法,这一行在Android 7.0及以下版本不是必须的,但是Android 8.0或者更高版本,发送广播的条件更加严苛,必须添加这一行内容。
intent.setComponent(new ComponentName("com.example.mytest", "com.example.mytest.BroadcastReceiverTest"));
//创建的ComponentName实例化对象有两个参数,第1个参数是指接收广播类的包名,第2个参数是指接收广播类的完整类名。
sendBroadcast(intent,null);

这样,你就可以接收到静态注册的广播了。

五、总结

    以上内容就是BroadcastReceiver的全部内容了,总结下来,个人认为关于BroadcastReceiver总共就三个部分,注册、发送、接收器,注册分为静态和动态,发送分为标准(无序)、有序、本地,接收器靠的是继承BroadcastReceiver类重写onReceive()方法。记住这些广播就不是难事了~