Android Broadcast 使用详解

783 阅读6分钟

1. 广播机制简介

Android 广播机制是一种在 Android 系统中用于在应用程序之间或应用程序的不同组件之间传递信息的重要通信机制。

广播(Broadcast)可以被理解为一种消息,当系统中发生某些事件(如系统启动、电量低、网络状态改变等)或应用程序执行某些操作时,会发送相应的广播。这些广播可以被其他应用程序或应用程序内的其他组件接收并响应。

Android 每个应用程序都可以对自己感兴趣的广播进行注册。发送广播借助 Intent。接收广播借助广播接收器(Broadcast Receiver)。

广播分为两种类型:标准广播有序广播

1. 标准广播: 异步执行。广播发出后,所有的广播接收器几乎会在同一时刻接收到这条广播信息,没有先后顺序。这种广播效率高,但无法截断。

2. 有序广播: 同步执行。广播发出后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,才能继续传递。有先后顺序,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播。

注册广播一般有两种方式:动态注册静态注册

2. 动态注册广播

以接收系统广播为例,介绍广播的动态注册。

Android 内置了很多系统级别的广播。比如手机开机完成后发出一条广播,网络变化,电量变化,时间改变等等。注册广播之后就可以接收广播。

动态注册广播是通过 Context 的 registerReceiver 方法进行注册,该方法接受两个参数:

// android/content/Context.java
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
                                            IntentFilter filter);
  • BroadcastReceiver:是一个用于接收和处理系统或应用发出的广播事件的组件(intent)。
  • IntentFilter:intent 过滤器,负责检查进入应用组件(如Activity、Service、BroadcastReceiver)的 intent,将符合要求的 intent 过滤出来。

它们共同实现了对广播消息的精准接收和处理。开发者通过为 BroadcastReceiver 配置 IntentFilter,可以指定它感兴趣的广播消息类型。这样,当系统或其他应用发出广播消息时,只有符合 IntentFilter 设置规则的广播消息才会被 BroadcastReceiver 接收,并触发相应的操作。

以监听网络变化为例,创建一个广播接收器 NetworkReceiver,继承 BroadcastReceiver,重写 onReceive()方法。有广播到来时,onReceive()方法就会执行:

public class NetWorkReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        if(networkInfo != null && networkInfo.isAvailable()){
            Toast.makeText(context,"network is available",Toast.LENGTH_SHORT).show();
            Log.i("NetWorkReceiver", "network is available");

        }else{
            Toast.makeText(context,"network is unavailable", Toast.LENGTH_SHORT).show();
            Log.i("NetWorkReceiver", "network is unavailable");
        }
    }
}

不要在 onReceive 方法中添加过多的逻辑或较长的耗时操作。

网络发生变化,系统发出一条值为 android.net.conn.CONNECTIVITY_CHANGE 的广播。创建一个 IntentFilter 实例,添加 android.net.conn.CONNECTIVITY_CHANGE 的 action。

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
NetWorkReceiver networkReceiver = new NetWorkReceiver();
registerReceiver(networkReceiver, intentFilter);

执行完 registerReceiver 方法后,当网络状态发生变化时 NetWorkReceiver 的 onReceive 方法就会收到回调。

动态注册的广播还需要调用 unregisterReceiver 方法取消注册。

须在配置文件里声明权限:

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

3. 静态注册广播

静态注册在 AndroidManifest.xml 中注册。

动态注册的广播接收器自由控制注册和注销,灵活,但必须在程序启动后才能接收到广播。若想在程序未启动的情况下接受广播,使用静态注册。

在 Android 8.0 系统之后,所有隐式广播都不允许用静态注册的方式来接收。

隐式广播指那些没有具体指定发给哪个应用程序的广播,大多数系统广播属于隐式广播,但少数特殊的系统广播目前仍然允许使用静态注册的方法接收。

以接收自定义广播为例,介绍如何静态注册广播。

首先创建 MyReceiver:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "My Broadcast", Toast.LENGTH_LONG).show();
        Log.i("MyReceiver", "My Broadcast");
    }
}

静态的广播接收器要在 AndroidManifest 中注册才可以使用。

<receiver
    android:name=".broadcast.MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.study.broadcast.MY_BROADCAST"/>
    </intent-filter>
</receiver>

可以在另一个进程中发送广播,可以发送标准广播或有序广播。

4. 发送广播

4.1 发送标准广播

通过 sendBroadcast 发送:

Intent intent = new Intent("com.example.test.broadcast.MY_BROADCAST");
intent.setPackage("com.example.test");
sendBroadcast(intent);

调用 setPackage()方法,指定发送给哪个应用程序,从而让它变成一条显示广播。还可以在 Intent 中携带一些数据传递给相应的 BroadcastReceiver。

发送广播还可以指定权限参数,接收器若要接收此广播,则必须在 Manifest 标记该权限。如果权限属于危险权限,必须先授予权限,接收器才能接收广播。

4.2 发送有序广播

通过 sendOrderedBroadcast,第二个参数是一个与权限相关的字符串,此处传入 null 就行:

Intent intent = new Intent("com.example.test.broadcast.MY_BROADCAST"); 
intent.setPackage("com.example.test");
sendOrderedBroadcast(intent, null);

在 onReceive()中调用 abortBroadcast(),表示将这条广播截断:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "My Broadcast", Toast.LENGTH_LONG).show();
        Log.i("MyReceiver", "My Broadcast");
        abortBroadcast();
    }
}

可以控制接收器的运行顺序。使用 android:priority 属性,具有相同优先级的接收器将按随机顺序运行。

<application>
    <!-- 其他应用组件 -->
 
    <!-- 广播接收器A,优先级设为最高 -->
    <receiver android:name=".ReceiverA">
        <intent-filter android:priority="1000">
            <action android:name="com.example.test.broadcast.MY_BROADCAST" />
        </intent-filter>
    </receiver>
 
    <!-- 广播接收器B,优先级设为中等 -->
    <receiver android:name=".ReceiverB">
        <intent-filter android:priority="500">
            <action android:name="com.example.test.broadcast.MY_BROADCAST" />
        </intent-filter>
    </receiver>
 
    <!-- 广播接收器C,优先级设为最低 -->
    <receiver android:name=".ReceiverC">
        <intent-filter>
            <action android:name="com.example.test.broadcast.MY_BROADCAST" />
        </intent-filter>
    </receiver>
 
    <!-- 其他应用组件 -->
</application>

在上面的例子中,当我们发送一个动作为com.example.test.broadcast.MY_BROADCAST的有序广播时,广播接收器的执行顺序将按照它们设置的优先级来决定:

  1. ReceiverA:由于其优先级最高(1000),它将第一个接收到广播并处理。
  2. ReceiverB:优先级为500,将在ReceiverA之后接收到广播。
  3. ReceiverC:由于没有设置优先级(默认为0),它将最后接收到广播。

4.3 发送包含权限的广播

发送方声明权限:

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

若要接收此广播,接收方应用必须请求如下权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

您可以指定现有的系统权限,也可以使用 <permission> 元素定义自定义权限。

接收方声明权限:

接收方有两种方式声明权限,通过 Manifest 或 registerReceiver 方法:

<receiver
    android:name=".broadcast.MyReceiver"
    android:permission="android.permission.ACCESS_COARSE_LOCATION"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.study.broadcast.MY_BROADCAST"/>
    </intent-filter>
</receiver>

registerReceiver(
        context, myBroadcastReceiver, filter,
        android.Manifest.permission.ACCESS_COARSE_LOCATION,
        null, 
        receiverFlags
);

那么,发送方应用必须请求如下权限,才能向这些接收器发送广播:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />