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 或其他条件来查询、更新或删除事件。

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

相关推荐
Devil枫2 小时前
Kotlin高级特性深度解析
android·开发语言·kotlin
ChinaDragonDreamer2 小时前
Kotlin:2.1.20 的新特性
android·开发语言·kotlin
雨白12 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹14 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空16 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭16 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日17 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安17 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑17 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟21 小时前
CTF Web的数组巧用
android