待办类应用作为一款提升工作效率的实用工具,在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 或其他条件来查询、更新或删除事件。
记住要妥善处理潜在的错误,例如权限拒绝或插入失败。