使用的工具
现在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);
}
}