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

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

相关推荐
阿巴斯甜12 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker12 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952713 小时前
Andorid Google 登录接入文档
android
黄林晴14 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android