08-Android中的广播机制

821 阅读5分钟

使用的工具

现在android项目使用的 android studio 来进行开发,开发的语言现在推荐的是 kotlin, 不过这里还是先使用 Java 来开发

  • AndroidStudio 版本: Android Studio Iguana | 2023.2.1
  • 项目语言: Java
  • JDK版本: 17

广播的类型

在 Android 中广播有两种类型

  • 标准广播:这是完全异步执行的广播,所有广播接收器之间接收消息时没有先后之分
  • 有序广播:是同步执行的,只有前面的广播接收器执行完成之后,后面的广播接收器才可能执行(前面的广播接收器可以截断消息的向下流传)

广播接收器注册方式

在 Android 中想要注册广播接收器有两种方式

  • 静态注册
    • 直接在 AndroidManifest.xml 中进行注册
    • 对于静态注册的广播,即使应用没有启动也是可以接收到对应的广播的
  • 动态注册
    • 通过代码来注册
    • 对于动态注册的广播,只有项目启动并且进入到对应的 Activity 才会启动注册,如果没有进入到应用对应的 Activity 那么广播是不会生效的,另外,当退出应用的对应界面后一般也是会取消注册广播的

广播发送和动态广播注册示例

示例中使用的类和布局文件结构

E:.
│  AndroidManifest.xml
│  
├─java
│  └─com
│      └─example
│          └─ademo
│              │  MainActivity.java
│
└─res
    ├─layout
    │      activity_main.xml

就一个 Android 主配置文件,主 Activity 和主布局,自定义的 BroadcastReceiver 也是在 MainActivity 中, 先看一下最终的效果

点击发送广播按钮之后,自定义的 BroadcastReceiver 可以接收到对应的消息

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools">  
  
  
    <application  
        android:allowBackup="true"  
        android:dataExtractionRules="@xml/data_extraction_rules"  
        android:fullBackupContent="@xml/backup_rules"  
        android:icon="@mipmap/ic_launcher"  
        android:label="@string/app_name"  
        android:roundIcon="@mipmap/ic_launcher_round"  
        android:supportsRtl="true"  
        android:theme="@style/Theme.ADemo"  
        tools:targetApi="31">  
          
        <activity  
            android:name=".MainActivity"  
            android:exported="true"  
            android:label="MainActivity">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    </application>  
  
</manifest>

可以看到在 AndroidManifest.xml 中并没有广播相关的注册,因为这个示例是动态广播的例子,只需要在代码中进行注册即可

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical">  
  
    <Button  
        android:id="@+id/btn_send"  
        android:text="点我发送广播"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"/>  
</LinearLayout>

在主布局文件中就一个按钮,项目启动后看到的界面就是这个

MainActivity

package com.example.ademo;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.util.Log;  
import android.view.View;  
import android.widget.Button;  
import android.widget.ListView;  
  
import androidx.annotation.Nullable;  
import androidx.appcompat.app.AppCompatActivity;  
import androidx.fragment.app.Fragment;  
import androidx.fragment.app.FragmentManager;  
  
import com.example.ademo.adapter.FruitAdapter;  
import com.example.ademo.vo.EachItem;  
  
import java.util.ArrayList;  
import java.util.List;  
  
public class MainActivity extends AppCompatActivity {  
  
    private BroadcastReceiver broadcastReceiver;  
  
    private static String CUS_ACTION = "test.action";  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity" + this);  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        IntentFilter intentFilter = new IntentFilter();  
        intentFilter.addAction(CUS_ACTION);  
        broadcastReceiver = new DynamicBroadCastReceiver();  
        // 注册自定义的广播接收器
        registerReceiver(broadcastReceiver, intentFilter, RECEIVER_EXPORTED);  
        // 给按钮添加点击事件
        findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent(CUS_ACTION);  
                // 点击按钮后会回调到这里,在这里进行广播的发送
                sendBroadcast(intent);  
            }  
        });  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        unregisterReceiver(broadcastReceiver);  
    }  
}  

// 自定义的广播接收器需要继承 BroadcastReceiver 类
class DynamicBroadCastReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
	    // 这里是广播接收的地方
        Log.i("DynamicBroadCastReceiver: 接收到的action: ", intent.getAction());  
    } 
}

在 MainActivity 中做了如下几件事情

  • 对按钮注册了点击事件
  • 当点击按钮时调用 sendBroadcast(这是父类的方法) 方法进行广播的发送
  • 自定义 DynamicBroadCastReceiver 类继承 BroadcastReceiver 作为广播接收器类,并且调用 registerReceiver 方法进行广播注册(动态注册方式)
  • 对于动态注册的方式在当前 Activity 被销毁的时候需要对注册过的广播进行取消注册

静态注册广播

对于静态注册就是在 AndroidManifest.xml 中使用 receiver 标签来注册,而且不需要在代码中使用 registerReceiver 来注册了,下面就是一个示例

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools">  
  
    <application  
        android:allowBackup="true"  
        android:dataExtractionRules="@xml/data_extraction_rules"  
        android:fullBackupContent="@xml/backup_rules"  
        android:icon="@mipmap/ic_launcher"  
        android:label="@string/app_name"  
        android:roundIcon="@mipmap/ic_launcher_round"  
        android:supportsRtl="true"  
        android:theme="@style/Theme.ADemo"  
        tools:targetApi="31">  
  
        <receiver android:name=".rece.DynamicBroadCastReceiver"  
            android:exported="true"  
            android:enabled="true">  
            <intent-filter>  
                <action android:name="test.action" />  
            </intent-filter>  
        </receiver>  
  
        <activity  
            android:name=".MainActivity"  
            android:exported="true"  
            android:label="MainActivity">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    </application>  
  
</manifest>

在 Android 8.0(API级别26)后发送静态广播需要注意的是发送前需要添加包名(否则是接收不到广播的),如下所示:

public class MainActivity extends AppCompatActivity {  
  
    private static String CUS_ACTION = "test.action";  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity" + this);  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent(CUS_ACTION);  
                // 这里需要添加包名
                intent.setPackage(getPackageName());  
                sendBroadcast(intent);  
            }  
        });  
    }  
}

有序广播

对于有序广播,有以下几点需要注意

发送方

  • 正常的广播发送使用的是 sendBroadcast(Intent) 方法
  • 有序广播使用的是 sendOrderedBroadcast 方法

广播接收器优先级的设置

  • 如下所示,在 receiver 标签中可以通过 android:priority 属性来设置优先级
  • 优先级设置的值是 -1000到1000 之间的整数,值越大优先级越高
<receiver android:name=".rece.DynamicBroadCastReceiver"  
    android:exported="true"  
    android:enabled="true"  
    android:priority="100">  
    <intent-filter>  
        <action android:name="test.action" />  
    </intent-filter>  
</receiver>

有序消息截断

public class DynamicBroadCastReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.i("DynamicBroadCastReceiver: 接收到的action: ", intent.getAction());  
        // 如果这是一个有序消息,通过调用 abortBroadcast 方法可以截断消息的往下传递
        abortBroadcast();  
    }  
}

本地广播

  • 以上讲解的都是系统级别广播,即发出的广播可以被任意程序接收到
  • 为了程序的安全性,也可以使用本地广播,本地广播的特点如下
    • 发出的广播只能在应用程序内部传递
    • 广播接收器也只能接收程序内部的广播
    • 本地广播是无法通过静态注册的方式来实现注册,只能通过代码进行动态注册

本地广播的使用

对于本地广播是通过 LocalBroadcastManager 类进行管理的,这里的管理是指本地广播的发送,注册,取消注册都是通过 LocalBroadcastManager 类进行,且看下面的示例

package com.example.ademo;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.util.Log;  
import android.view.View;  
  
import androidx.appcompat.app.AppCompatActivity;  
import androidx.localbroadcastmanager.content.LocalBroadcastManager;  
  
import com.example.ademo.rece.DynamicBroadCastReceiver;  
  
public class MainActivity extends AppCompatActivity {  
  
    private static String CUS_ACTION = "test.action";  
  
    private LocalBroadcastManager localBroadcastManager;  
  
    private DynamicBroadCastReceiver receiver;  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity" + this);  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        IntentFilter intentFilter = new IntentFilter(CUS_ACTION);  
        localBroadcastManager = LocalBroadcastManager.getInstance(this);  
        receiver = new DynamicBroadCastReceiver();  
        // 静态广播的注册,需要注意的是 DynamicBroadCastReceiver 并没有在 AndroidManifest 中注册
        localBroadcastManager.registerReceiver(receiver, intentFilter);  
  
        findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent(CUS_ACTION);  
                intent.setPackage(getPackageName());  
                // 发送静态广播
                localBroadcastManager.sendBroadcast(intent);  
            }  
        });  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        // 静态广播的取消注册
        localBroadcastManager.unregisterReceiver(receiver);  
    }  
}