Android 待办类应用提醒功能的实现及其问题

待办类应用作为一款提升工作效率的实用工具,在Android平台上深受用户喜爱。其核心功能之一便是提醒功能,帮助用户及时完成待办事项。

Android待办类应用的提醒功能看似简单,但涉及到多种系统机制和细节处理,需要开发者仔细考量和优化。

方式一:AlarmManager

设置时间点时的闹钟,主要通过 AlarmManager 的 setExact 方法在 reminderTime 时间点触发发送广播,从而在应用的广播接收器中处理弹出提醒通知。

java 复制代码
    public static void setAlarm(Context context, AlarmManager alarmManager, TodoItem todoItem, long reminderTime) {
        Intent intent = new Intent(context, AlarmReceiver.class);
        AlarmIntentParams alarmIntentParams = new AlarmIntentParams(todoItem);
        intent.putExtra(AlarmReceiver.KEY_ALARM_PARAMS, AlarmIntentParams.toByteArray(alarmIntentParams));
        intent.setAction(AlarmReceiver.ACTION_ALARM_TODO_NOTIFICATION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, alarmIntentParams.hashCode(),
                intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
    }

因为可能同时设置多个闹钟及其通知,需要构造唯一的 id ,这里通过alarmIntentParams.hashCode() 方法实现 ,主要参数为 AlarmIntentParams 的属性合集的 hash 值

java 复制代码
public int hashCode() {
        return Objects.hash(id, content, time, repeat, important);
}

取消定时的闹钟,需要构造设置闹钟时完全一样的PendingIntent

java 复制代码
    public static void cancelAlarm(Context context, AlarmManager alarmManager, TodoItem todoItem, long reminderTime) {
        Intent intent = new Intent(context, AlarmReceiver.class);
        AlarmIntentParams alarmIntentParams = new AlarmIntentParams(todoItem);
        intent.putExtra(AlarmReceiver.KEY_ALARM_PARAMS, AlarmIntentParams.toByteArray(alarmIntentParams));
        intent.setAction(AlarmReceiver.ACTION_ALARM_TODO_NOTIFICATION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, alarmIntentParams.hashCode(),
                intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        alarmManager.cancel(pendingIntent);
    }

在广播接收器中做提醒通知

java 复制代码
public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        String action = intent.getAction();
        android.util.Log.e("maxx", "AlarmReceiver action=" + action);
        NotificationManager mTodoNotificationManager = context.getSystemService(NotificationManager.class);
        if(action.equals(ACTION_ALARM_TODO_NOTIFICATION)){
            byte[] b = intent.getByteArrayExtra(KEY_ALARM_PARAMS);
            AlarmIntentParams params = AlarmIntentParams.toParcelable(b,AlarmIntentParams.CREATOR);
            // 通过获取的参数,显示对应的通知内容,及其通知的按钮Action事件
        }
   }
}

只需要在 AndroidManifest.xml 中声明 AlarmReceiver 组件即可,无需额外配置

xml 复制代码
<receiver android:name=".AlarmReceiver" />

此类实现会有2个问题:

问题1:实现的提醒铃声是集成在通知中的,由系统通知自己实现,无法自己控制铃声的暂停或播放,也无法抢夺音频焦点,在其他应用来通知,并同样有铃声,此时我们的通知铃声就自动停止播放,并且不再恢复。在调节音量的时,我们的通知铃声也会自动停止。

问题2:在应用被清理后台后,AlarmManager 的 setExact 设置的闹钟不再发送广播,通知功能失效,需要添加android:sharedUserId="android.uid.system",将赋予 system sharedUserId 的应用在被杀掉进程后设置的闹钟仍发送广播,当然这需要内置应用打系统才行。在手机重启后,通知也会功能失效,因为在重启后所有 AlarmManager 设置的闹钟都会被重置失效,需要监听开机广播,重新设置闹钟。

当然还可以通过假的清理后台来骗过用户实现,最近任务应用中添加白名单,将需要的应用添加到白名单中即可不杀掉应用进程。

方式二:通过日历

要在 Android 应用中向日历添加事件,您需要使用日历提供程序 API,该 API 提供对日历数据的访问,并允许您创建、修改和删除事件。以下是分步指南:

1. 请求日历权限:

  • 在您的应用的清单文件中,添加访问日历所需的权限:
xml 复制代码
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />

2. 检查日历访问:

  • 在访问日历之前,请验证用户是否已授予所需的权限。您可以使用checkSelfPermission()方法来检查权限。
java 复制代码
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED ||
    ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR}, REQUEST_CODE_CALENDAR_PERMISSIONS);
    } else {
        // Proceed with calendar access
    }

3.创建ContentResolver:

  • 获取一个ContentResolver对象来与日历提供程序交互。
java 复制代码
ContentResolver contentResolver = getContentResolver();

4. 构建活动内容价值:

  • 创建一个ContentValues对象来存储事件的详细信息。
java 复制代码
ContentValues eventValues = new ContentValues();

eventValues.put(CalendarContract.Events.CALENDAR_ID, DEFAULT_CALENDAR_ID); // Replace with your calendar ID
eventValues.put(CalendarContract.Events.TITLE, "Event Title");
eventValues.put(CalendarContract.Events.DESCRIPTION, "Event Description");
eventValues.put(CalendarContract.Events.EVENT_LOCATION, "Event Location");

// Set start and end times in milliseconds
long startTimeMillis = System.currentTimeMillis();
long endTimeMillis = startTimeMillis + 3600000; // 1 hour in milliseconds

eventValues.put(CalendarContract.Events.DTSTART, startTimeMillis);
eventValues.put(CalendarContract.Events.DTEND, endTimeMillis);

eventValues.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());

5.插入事件:

  • 使用ContentResolver将事件插入日历。
java 复制代码
Uri calendarUri = CalendarContract.Events.CONTENT_URI;
Uri eventUri = contentResolver.insert(calendarUri, eventValues);

if (eventUri != null) {
    // Event successfully added
} else {
    // Handle insertion failure
}

6.附加选项:

  • 您可以通过向对象添加相应的值来为事件添加更多详细信息,例如参加者、提醒和警报ContentValues
  • 您可以使用ContentResolver根据事件的 ID 或其他条件来查询、更新或删除事件。

记住要妥善处理潜在的错误,例如权限拒绝或插入失败。

相关推荐
恋猫de小郭2 小时前
Android Studio 正式版 10 周年回顾,承载 Androider 的峥嵘十年
android·ide·android studio
aaaweiaaaaaa4 小时前
php的使用及 phpstorm环境部署
android·web安全·网络安全·php·storm
工程师老罗6 小时前
Android记事本App设计开发项目实战教程2025最新版Android Studio
android
pengyu10 小时前
系统化掌握 Dart 编程之异常处理(二):从防御到艺术的进阶之路
android·flutter·dart
消失的旧时光-194311 小时前
android Camera 的进化
android
基哥的奋斗历程12 小时前
Openfga 授权模型搭建
android·adb
Pakho love1 天前
Linux:文件与fd(被打开的文件)
android·linux·c语言·c++
勿忘初心911 天前
Android车机DIY开发之软件篇(九) NXP AutomotiveOS编译
android·arm开发·经验分享·嵌入式硬件·mcu
lingllllove1 天前
PHP中配置 variables_order详解
android·开发语言·php
消失的旧时光-19431 天前
Android-音频采集
android·音视频