四大组件之BroadcastReceiver

参考:Android 四大组件

参考:BroadcastReceiver广播接收器

BroadcastReceiver 简介

广播的生命周期很短(调用对象–>回调 onReceive –>结束),广播对象也是在应用进程的主线程中被构造,不适合耗时的操作,也不推荐开启新线程,因为往往线程还没结束,广播对象就已经执行完毕而被系统销毁了。每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。

BroadcastReceiver 注册

注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为动态注册,后者称为静态注册。

使用步骤:

  1. 自定义一个类继承 BroadcastReceiver

  2. 重写 onReceive 方法

  3. 注册广播

  4. 不再使用时,取消注册广播

动态注册方式

案例使用(监听网络状态变化):

第一和第二步:

public class NetworkChangeReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "network change", Toast.LENGTH_LONG).show();
    }

}

第三步:

mNetworkChangeReceiver = new NetworkChangeReceiver();
//动态注册:创建一个IntentFilter的实例,添加网络变化的广播(功能是对组件进行过滤,只获取需要的消息)
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(mNetworkChangeReceiver, intentFilter);

第四步:

unregisterReceiver(netWorkChangeReceiver);

静态注册方式

动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是通过代码方式实现的。为了让程序在未启动的情况下就能接收到广播,这里就需要使用到静态注册。

案例使用:

第一和第二步:

public class TestReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //测试 10s 内没执行完,会不会造成 ANR
//        for (int i = 0; i < Integer.MAX_VALUE; i++) {
//            i--;
//        }
        Toast.makeText(context, "TestReceiver", Toast.LENGTH_LONG).show();
    }

}

第三步:

<receiver android:name=".receiver.TestReceiver">
    <intent-filter>
        <action android:name="zeng.fanda.com.fourmoduledemo.test"/>
    </intent-filter>
</receiver>

注意: Android 8.0 的广播对静态注册做了限制,需要用动态注册的方式,不然会报 Background execution not allowed(不允许后台执行)的日志信息,广播也无法正常工作。

BroadcastReceiver 发送

广播被分为两种不同的类型:普通广播(无序广播或标准广播)和有序广播。

特点

  • 无序广播:
  1. 完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高。

  2. 接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播。

  • 有序广播:
  1. 有序广播是按照接收者声明的优先级别来传递的,被接收者依次接收广播。接收者可以拦截广播来终止向下传播,也可以存入数据来传递给下一个接收者,即下一个广播接收者可以获取到上一个广播存入的数据。

发送无序广播

//广播一
public class TestReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.i("TestReceiver-1");
    }
}

//广播二
public class TestReceiver2 extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.i("TestReceiver-2");
    }
}

//同时注册相同的过滤信息
<receiver android:name=".receiver.TestReceiver">
    <intent-filter>
        <action android:name="zeng.fanda.com.fourmoduledemo.test" />
    </intent-filter>
</receiver>
<receiver android:name=".receiver.TestReceiver2">
    <intent-filter>
        <action android:name="zeng.fanda.com.fourmoduledemo.test" />
    </intent-filter>
</receiver>

//发送广播
sendBroadcast(new Intent("zeng.fanda.com.fourmoduledemo.test"));

结果如下:

发送有序广播

//广播一
public class TestReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //拦截后终止广播的传递
        abortBroadcast();
        Logger.i("TestReceiver-1");
    }
}

//广播二
public class TestReceiver2 extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.i("TestReceiver-2");
    }
}

//同时注册相同的过滤信息
<receiver android:name=".receiver.TestReceiver">
    // 设置广播优化级为1000
    <intent-filter android:priority="1000">
        <action android:name="zeng.fanda.com.fourmoduledemo.test" />
    </intent-filter>
</receiver>
<receiver android:name=".receiver.TestReceiver2">
    // 设置广播优化级为500
    <intent-filter android:priority="500">
        <action android:name="zeng.fanda.com.fourmoduledemo.test" />
    </intent-filter>
</receiver>

//第二个参数是一个与权限相关的字符串,这里传入null即可
sendOrderedBroadcast(new Intent("zeng.fanda.com.fourmoduledemo.test"),null);

结果如下:

由于广播一的优先级大于广播二,所以广播一先收到广播,并且广播一调用了 abortBroadcast() 方法来终止了广播的传递,所以广播二是收不到广播的,自然就不打印日志了。

上述通过 android:priority 属性给广播接收器设置了优先级。这个属性的范围在 -1000 到 1000 ,数值越大,优先级越高。也可以动态地在代码调用 IntentFilter 对象的 setPriority() 进行设置。

我们也可以在广播一中通过 setResultXXX 方法存入一些数据,在广播二中通过 getResultXXX 方法获取到广播一中的数据。如果还有广播三四五等,都可以获取到上一个广播通过 setResultXXX 方法存入的数据,比如广播三比广播二优化级低,广播二也设置过数据,则广播三只能拿到广播二存入的数据。

本地广播

之前我们发送和接收的广播全部都属于全局广播,即发出去的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样一来,必然会造成安全问题。于是便有了本地广播:即只能在本应用程序中发送和接收广播。只用使用 LocalBroadcastManager 类对广播进行收发就可以了。

//获取实例
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);

//注册
mLocalBroadcastManager.registerReceiver(mNetworkChangeReceiver,intentFilter);

//发送
mLocalBroadcastManager.sendBroadcast(new Intent("android.net.conn.CONNECTIVITY_CHANGE"));

//反注册
mLocalBroadcastManager.unregisterReceiver(mNetworkChangeReceiver);

注意:本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定已经启动了,没有必要使用到静态注册的功能。

静态注册和动态注册区别

  • 动态注册广播不是常驻型广播,也就是说广播跟随 activity 的生命周期。注意: 在 activity 结束前,移除广播接收器。
    静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

  • 当广播为有序广播时:

    1. 优先级高的先接收
    2. 同优先级的广播接收器,动态优先于静态
    3. 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
  • 当广播为普通广播时:

    1. 无视优先级,动态广播接收器优先于静态广播接收器
    2. 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。