1、发送/接收临时广播
广播用于 Android 组件之间的灵活通信,与 Activity 的区别在于:
- Activity 只能一对一通信;Broadcast 可以一对多,一人发送广播,多人接收处理;
- 对于发送者来说,广播不需要考虑接收者有没有在工作,接收者在工作就接收广播,不在工作就丢弃广播;
- 对于接收者来说,会收到各式各样的广播,所以接收者要自行过滤符合条件的广播,才能进行解包处理。
与广播有关的方法主要有 3 个:
- sendBroadcast:发送广播。
- registerReceiver:注册接收器,一般在 onStart 或 onResume 方法中注册。
- unregisterReceiver:注销接收器,一般在 onStop 或 onPause 方法中注销。
如果广播是在应用内使用,不需要跨进程,建议使用 LocalBroadcastManager 下的 registerReceiver 与 unregisterReceiver 方法,这样不但更有效率(不需要跨进程通信),还不用考虑广播开放造成的安全问题。
下面对广播的工作流程进行具体演示。现在 Fragment 内有一个 Spinner 下拉框,可选择背景颜色,一旦选中某个背景色,整个活动页面的背景色就换成新颜色。Fragment 内部发现选中颜色后,要发送一个背景色变更的广播,代码如下:
// 声明一个广播事件的标识串
public final static String EVENT = "com.example.senior.fragment.BroadcastFragment";
// 声明一个颜色名称数组
private String[] mColorNameArray = {"红色", "黄色", "绿色", "青色", "蓝色"};
// 声明一个颜色类型数组
private int[] mColorIdArray = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE};
// 定义一个与下拉框配套的颜色选择监听器
class ColorSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if (!bFirst || mColorSeq != arg2) { // 如果要改变背景色
mColorSeq = arg2;
// 创建一个广播事件的意图
Intent intent = new Intent(BroadcastFragment.EVENT);
intent.putExtra("seq", arg2);
intent.putExtra("color", mColorIdArray[arg2]);
// 通过本地的广播管理器来发送广播
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
bFirst = false;
}
public void onNothingSelected(AdapterView<?> arg0) {}
}
同时,Activity 代码要实现背景色变更的广播接收器。一旦接收到背景色变更的广播,就立即修改页面为最新的背景色,示例代码如下:
public void onStart() {
super.onStart();
initSpinner();
// 创建一个背景色变更的广播接收器
bgChangeReceiver = new BgChangeReceiver();
// 创建一个意图过滤器,只处理指定事件来源的广播
IntentFilter filter = new IntentFilter(BroadcastFragment.EVENT);
// 注册广播接收器,注册之后才能正常接收广播
LocalBroadcastManager.getInstance(mContext).registerReceiver(bgChangeReceiver, filter);
}
@Override
public void onStop() {
// 注销广播接收器,注销之后就不再接收广播
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(bgChangeReceiver);
super.onStop();
}
// 声明一个背景色变更的广播接收器
private BgChangeReceiver bgChangeReceiver;
// 定义一个广播接收器,用于处理背景色变更事件
private class BgChangeReceiver extends BroadcastReceiver {
// 一旦接收到背景色变更的广播,马上触发接收器的onReceive方法
public void onReceive(Context context, Intent intent) {
if (intent != null) {
// 从广播消息中取出最新的颜色序号
mColorSeq = intent.getIntExtra("seq", 0);
// 设置下拉框默认显示该序号项
sp_bg.setSelection(mColorSeq);
}
}
}
2、定时器 AlarmManager
AlarmManager 是 Android 提供的一个全局定时器,利用系统闹钟定时发送广播。这样如果 App 提前注册闹钟的广播接收器,即使 App 退出了,只要定时到达,App 就会被唤醒响应广播事件。
下面具体演示下过程,首先在页面代码中通过 AlarmManager 设置闹钟:
public void onClick(View v) {
if (v.getId() == R.id.btn_alarm) {
// 创建一个广播事件的意图
Intent intent = new Intent(ALARM_EVENT);
// 创建一个用于广播的延迟意图
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 从系统服务中获取闹钟管理器
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
// 给当前时间加上若干秒
calendar.add(Calendar.SECOND, mDelay);
// 开始设定闹钟,延迟若干秒后,携带延迟意图发送闹钟广播
alarmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
mDesc = DateUtil.getNowTime() + " 设置闹钟";
tv_alarm.setText(mDesc);
}
}
然后在页面代码中定义一个广播接收器 AlarmReceiver :
// 声明一个闹钟广播事件的标识串
private String ALARM_EVENT = "com.example.senior.AlarmActivity.AlarmReceiver";
private static String mDesc = ""; // 闹钟时间到达的描述
private static boolean isArrived = false; // 闹钟时间是否到达
// 定义一个闹钟广播的接收器
public static class AlarmReceiver extends BroadcastReceiver {
// 一旦接收到闹钟时间到达的广播,马上触发接收器的onReceive方法
public void onReceive(Context context, Intent intent) {
if (intent != null) {
Log.d(TAG, "AlarmReceiver onReceive");
if (tv_alarm != null && !isArrived) {
isArrived = true;
mDesc = String.format("%s\n%s 闹钟时间到达", mDesc, DateUtil.getNowTime());
tv_alarm.setText(mDesc);
}
}
}
}
接着打开 AndroidManifest.xml,在 application 节点下增加广播接收器的声明(Android 9.0 以前):
<receiver android:name=".AlarmActivity$AlarmReceiver" >
<intent-filter>
<action android:name="com.example.senior.AlarmActivity.AlarmReceiver" />
</intent-filter>
</receiver>
Android 9.0 开始,需要在代码里声明(动态注册):
// 适配Android9.0开始
@Override
public void onStart() {
super.onStart();
// 从Android9.0开始,系统不再支持静态广播,应用广播只能通过动态注册
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// 创建一个闹钟的广播接收器
alarmReceiver = new AlarmReceiver();
// 创建一个意图过滤器,只处理指定事件来源的广播
IntentFilter filter = new IntentFilter(ALARM_EVENT);
// 注册广播接收器,注册之后才能正常接收广播
registerReceiver(alarmReceiver, filter);
}
}
@Override
public void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// 注销广播接收器,注销之后就不再接收广播
unregisterReceiver(alarmReceiver);
}
}
- PendingIntent 的意思是延迟的意图,只要不是立即传递的消息,都要用 PendingIntent,PendingIntent 调用了 getBroadcast 方法,表示这次携带的消息用于发送广播。
- AlarmManager 的 set 方法用于设置一次性定时器,
alarmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
该方法的第一个参数表示定时器类型(一般是 AlarmManager.RTC_WAKEUP,表示定时器即使在睡眠状态也会启动),第二个参数表示任务的执行时间,第三个参数表示携带消息的延迟任务(getBroadcast 返回的 PendingIntent 对象)。