使用 AlarmManager 结合广播接收器来实现定时检查

2024-05-30 00:20

本文主要是介绍使用 AlarmManager 结合广播接收器来实现定时检查,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用 AlarmManager 结合广播接收器来实现定时检查。这种方式在特定时间点触发广播,然后在广播接收器中检查时间。这样可以避免持续的轮询检查减少对系统资源的消耗。

以下是一个示例代码:

  1. 创建一个 BroadcastReceiver 用于接收 AlarmManager 的广播。
  2. BroadcastReceiver 中检查当前时间是否达到目标时间点。
  3. 如果达到目标时间点,则执行相应的操作,例如播放音频。

示例代码如下:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;public class TimeCheckReceiver extends BroadcastReceiver {private String targetTime = "12:00"; // 替换为你的目标时间点@Overridepublic void onReceive(Context context, Intent intent) {// 检查当前时间是否等于目标时间点try {SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");String currentTime = dateFormat.format(new Date());if (currentTime.equals(targetTime)) {// 如果达到目标时间点,执行相应的操作,例如播放音频playAudio(context);}} catch (Exception e) {e.printStackTrace();}}private void playAudio(Context context) {// 在这里实现播放音频的逻辑Toast.makeText(context, "Reached target time, playing audio", Toast.LENGTH_SHORT).show();MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.your_audio_file); // 替换为你的音频文件资源mediaPlayer.start();}
}

然后,在你的应用中设置 AlarmManager 来触发广播,示例代码如下:

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;public class AlarmHelper {public static void setAlarm(Context context) {AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);Intent intent = new Intent(context, TimeCheckReceiver.class);PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);// 设置每天的目标时间点,这里使用12:00作为示例Calendar calendar = Calendar.getInstance();calendar.set(Calendar.HOUR_OF_DAY, 12);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);// 设置每天触发一次alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);}
}

最后,确保在 AndroidManifest.xml 中注册广播接收器和权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><receiver android:name=".TimeCheckReceiver" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
</receiver>

相比于持续轮询检查当前时间的方法,使用 AlarmManager 和广播接收器的方式消耗的资源要少一些。因为它是在特定时间点触发广播,而不是持续在后台运行。

具体而言,这种方法的资源消耗主要集中在以下几个方面:

  1. AlarmManagerAlarmManager 是 Android 系统提供的一种服务,它的资源消耗相对较低。它会在设定的时间点触发广播,而不会持续占用大量的系统资源。

  2. 广播接收器:广播接收器会在触发广播时被系统唤醒,并执行相应的操作。这个过程消耗的资源相对较少,因为它只在接收到广播时才会被激活。

  3. 播放音频:如果你在广播接收器中播放音频,那么播放音频会消耗一定的系统资源,特别是在音频文件较大或者音频播放时长较长的情况下。但相比持续轮询检查当前时间,这种消耗要小得多。

综上所述,使用 AlarmManager 和广播接收器的方式消耗的系统资源相对较少,特别是对于周期性的任务,它是一种更加高效的方式。但是,你仍然需要注意合理使用资源,避免在广播接收器中执行耗时操作,以免影响用户体验和设备性能。

TimeCheckReceiver 广播接收器不一定需要在设备启动时接收广播。你可以根据你的需求来注册广播接收器。如果你希望在特定时间点触发广播,那么只需要在相应的时刻设置 AlarmManager 来触发广播即可,无需在设备启动时注册。

通常情况下,如果你的应用仅在特定时间点执行某些任务,那么在合适的时候注册广播接收器就可以了,而无需在设备启动时注册。这样可以避免不必要的广播接收器注册,减少资源消耗。

如果你有多个时间点需要监测并执行相应的任务,你可以稍作修改,在 setAlarm 方法中设置多个定时任务,每个定时任务对应一个时间点。

以下是一个修改后的示例代码,用于处理多个时间点:

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;public class AlarmHelper {private static final String TIME_POINT_1 = "08:00";private static final String TIME_POINT_2 = "12:00";public static void setAlarms(Context context) {setAlarm(context, TIME_POINT_1);setAlarm(context, TIME_POINT_2);// 如果有更多的时间点,可以继续添加 setAlarm 方法的调用}private static void setAlarm(Context context, String targetTime) {AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);Intent intent = new Intent(context, TimeCheckReceiver.class);intent.putExtra("target_time", targetTime); // 将目标时间点传递给广播接收器PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);// 设置每天的目标时间点Calendar calendar = Calendar.getInstance();calendar.set(Calendar.HOUR_OF_DAY, getHour(targetTime));calendar.set(Calendar.MINUTE, getMinute(targetTime));calendar.set(Calendar.SECOND, 0);// 设置每天触发一次alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);}private static int getHour(String time) {String[] parts = time.split(":");return Integer.parseInt(parts[0]);}private static int getMinute(String time) {String[] parts = time.split(":");return Integer.parseInt(parts[1]);}
}

在广播接收者中获取 targetTime 的数值,你可以通过 Intent 对象来获取。在 TimeCheckReceiveronReceive 方法中,你可以调用 getIntent() 方法获取触发广播的 Intent,然后从 Intent 中获取传递的 target_time 参数。

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;public class TimeCheckReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 从Intent中获取传递的目标时间点参数String targetTime = intent.getStringExtra("target_time");// 检查当前时间是否等于目标时间点try {SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");String currentTime = dateFormat.format(new Date());if (currentTime.equals(targetTime)) {// 如果达到目标时间点,执行相应的操作,例如播放音频playAudio(context);}} catch (Exception e) {e.printStackTrace();}}private void playAudio(Context context) {// 在这里实现播放音频的逻辑Toast.makeText(context, "Reached target time, playing audio", Toast.LENGTH_SHORT).show();// 如果需要,在这里可以获取其他参数,例如音频文件路径,然后播放音频}
}

如果你有多个时间点,你可以在 Intent 中传递一个标识来指示是哪个时间点触发了广播。然后在 TimeCheckReceiver 中根据这个标识来判断是哪个时间点触发了广播,并执行相应的操作。

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;public class TimeCheckReceiver extends BroadcastReceiver {private static final String EXTRA_TIME_POINT = "time_point";@Overridepublic void onReceive(Context context, Intent intent) {// 从Intent中获取传递的时间点标识参数int timePoint = intent.getIntExtra(EXTRA_TIME_POINT, -1);// 获取对应的目标时间点String targetTime = "";switch (timePoint) {case 1:targetTime = "08:00";break;case 2:targetTime = "12:00";break;// 如果有更多的时间点,继续添加 case}// 检查当前时间是否等于目标时间点try {SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");String currentTime = dateFormat.format(new Date());if (currentTime.equals(targetTime)) {// 如果达到目标时间点,执行相应的操作,例如播放音频playAudio(context);}} catch (Exception e) {e.printStackTrace();}}private void playAudio(Context context) {// 在这里实现播放音频的逻辑Toast.makeText(context, "Reached target time, playing audio", Toast.LENGTH_SHORT).show();// 如果需要,在这里可以获取其他参数,例如音频文件路径,然后播放音频}
}

在这个示例中,我们定义了一个名为 EXTRA_TIME_POINT 的常量来作为传递时间点标识的键。然后在 TimeCheckReceiveronReceive 方法中通过 intent.getIntExtra(EXTRA_TIME_POINT, -1) 来获取传递的时间点标识参数。根据不同的时间点标识,我们就可以判断是哪个时间点触发了广播,并执行相应的操作了。

在设置定时任务时,你需要在 AlarmHelper 类中的 setAlarm 方法中传递时间点标识参数,以区分不同的时间点。

代码补充(其实)以及一些说明:

为了使用 AlarmManager 设置定时任务并接收广播,通常需要在 AndroidManifest.xml 中声明相应的权限和广播接收器。以下是需要添加的权限和广播接收器的声明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.yourapp"><!-- 权限声明 --><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.SET_ALARM" /><!-- 应用组件声明 --><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><!-- 广播接收器声明 --><receiver android:name=".TimeCheckReceiver" /><!-- 其他应用组件 --></application>
</manifest>

权限说明

  • android.permission.WAKE_LOCK:允许应用程序使用 WakeLock 以确保在执行任务时设备不会进入休眠状态。这在某些情况下(例如长时间任务)可能需要。
  • android.permission.SET_ALARM:允许应用程序设置闹钟。这在某些旧版本的 Android 中是必要的,但在较新的版本中已经不再需要显式声明。

闹钟触发事件:

可以将 AlarmManager.setRepeating 改为 AlarmManager.setExactAlarmManager.setExactAndAllowWhileIdle 来确保闹钟只在特定时间点触发一次。这种方法可以减少系统资源消耗,并确保在特定时间点准确触发。

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import java.util.Calendar;public class AlarmHelper {public static void setAlarms(Context context, String openTime, String closeTime) {setAlarm(context, openTime, 1);setAlarm(context, closeTime, 2);}private static void setAlarm(Context context, String targetTime, int style) {AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);Intent intent = new Intent(context, TimeCheckReceiver.class);intent.putExtra("target_time", targetTime);intent.putExtra("time_style", style);// 使用不同的请求码来创建不同的 PendingIntentPendingIntent pendingIntent = PendingIntent.getBroadcast(context, style, intent, PendingIntent.FLAG_UPDATE_CURRENT);// 设置每天的目标时间点Calendar calendar = Calendar.getInstance();calendar.set(Calendar.HOUR_OF_DAY, getHour(targetTime));calendar.set(Calendar.MINUTE, getMinute(targetTime));calendar.set(Calendar.SECOND, 0);// 检查设置的时间是否已过,如果已过则增加一天if (calendar.getTimeInMillis() < System.currentTimeMillis()) {calendar.add(Calendar.DAY_OF_YEAR, 1);}// 设置每天触发一次alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);}private static int getHour(String time) {String[] parts = time.split(":");return Integer.parseInt(parts[0]);}private static int getMinute(String time) {String[] parts = time.split(":");return Integer.parseInt(parts[1]);}
}

说明

  1. AlarmManager.setExactAndAllowWhileIdle:这种方法确保闹钟在设定时间准确触发,即使设备处于低电量模式。
  2. 每天重新设置闹钟:在 TimeCheckReceiver 中,每次闹钟触发时都会重新设置下一天的闹钟,确保每天都会触发。
  3. 检查和调整时间:确保 targetTime 是有效的时间字符串格式("HH")。

通过这种方式,每天只会触发一次闹钟,减少系统资源消耗。确保在实际测试时设备的时间和时区设置正确,以避免由于时间差异导致的触发问题。

时间偏差问题:

这样已经基本上解决了,但是还会遇到这样一个问题,时间不对,相差1的问题,

获取时间时少一秒的问题通常不是由于 SimpleDateFormat 格式化的问题,而是由于时间获取和格式化的过程中的微小延迟。在运行时,创建 Date 对象和格式化这个对象之间可能有一个非常短的时间间隔,可能导致当前时间在实际时间上有几毫秒的偏差,从而在格式化输出时显示少了一秒。

要解决这个问题,可以尝试以下几种方法:

  1. 检查代码执行时间:确保从获取 Date 对象到格式化这个对象之间没有不必要的延迟。
  2. 使用系统时钟:直接获取系统时钟时间。

以下是一些代码示例,演示如何减少这种时间差异:

示例1:检查执行时间

确保代码执行中没有不必要的延迟:

import java.text.SimpleDateFormat;
import java.util.Date;public class TimeUtils {public static final SimpleDateFormat sdf3 = new SimpleDateFormat("HH:mm:ss");public static String getCurrentTime() {return sdf3.format(new Date());}
}public class Main {public static void main(String[] args) {long start = System.currentTimeMillis();String currentTime = TimeUtils.getCurrentTime();long end = System.currentTimeMillis();System.out.println("Current time: " + currentTime);System.out.println("Time taken: " + (end - start) + " ms");}
}

示例2:使用 DateTimeFormatterLocalTime

Java 8 及以上版本推荐使用 DateTimeFormatterLocalTime,它们是线程安全且更高效:

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;public class TimeUtils {private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");public static String getCurrentTime() {return LocalTime.now().format(timeFormatter);}
}public class Main {public static void main(String[] args) {String currentTime = TimeUtils.getCurrentTime();System.out.println("Current time: " + currentTime);}
}

示例3:减少格式化延迟

通过直接使用系统时钟减少时间偏差:

import java.text.SimpleDateFormat;
import java.util.Date;public class TimeUtils {public static final SimpleDateFormat sdf3 = new SimpleDateFormat("HH:mm:ss");public static String getCurrentTime() {long currentTimeMillis = System.currentTimeMillis();Date date = new Date(currentTimeMillis - (currentTimeMillis % 1000)); // 去掉毫秒部分return sdf3.format(date);}
}public class Main {public static void main(String[] args) {String currentTime = TimeUtils.getCurrentTime();System.out.println("Current time: " + currentTime);}
}

结论

  • 时间偏差:从获取 Date 对象到格式化过程中可能存在的时间延迟会导致时间偏差。
  • 优化时间获取和格式化过程:使用系统时钟或现代的时间 API (LocalTimeDateTimeFormatter) 可以减少这种偏差。
  • 去掉毫秒部分:在计算当前时间时去掉毫秒部分,确保获取的时间是精确的秒。

这些方法可以有效减少获取时间时的微小延迟问题。

这篇关于使用 AlarmManager 结合广播接收器来实现定时检查的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1015054

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义