一、APP 使用
闹钟设置步骤:
- 定义一个 PendingIntent,用户闹钟到期时触发相应动作(发送广播、启动服务等);
- 调用 AlarmManager 的 set 函数设置闹钟;
- 对于第一步使用广播方式,需要定义广播接收器,捕捉闹钟到期广播;
1、时钟类型
Android framework 中定义的时钟类型:
public static final int RTC_WAKEUP = 0;
public static final int RTC = 1;
public static final int ELAPSED_REALTIME_WAKEUP = 2;
public static final int ELAPSED_REALTIME = 3;
对应的 Native 中使用的时钟类型:
static const clockid_t android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
CLOCK_REALTIME_ALARM,
CLOCK_REALTIME,
CLOCK_BOOTTIME_ALARM,
CLOCK_BOOTTIME,
CLOCK_MONOTONIC,
CLOCK_REALTIME,
};
上层时钟类型作为索引,在 android_alarm_to_clockid 表中取到的时钟类型,即为底层使用的时钟。比如上层时钟类型为 ELAPSED_REALTIME_WAKEUP,那么底层时钟类型为 CLOCK_BOOTTIME_ALARM。实际使用过程中 RTC_WAKEUP、RTC 两种类型,会在 framework 层中转换层 ELAPSED_REALTIME_WAKEUP、ELAPSED_REALTIME 时钟。
RTC 表示 Unix 时间,ELAPSED_REALTIME 表示系统启动到现在经过的时间。两种时间都 WAKEUP 的版本,表示可以唤醒系统。
2、使用实例
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1,
new Intent(context, AlarmReceiver.class).setAction("testtime"), PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 15000 * 60 * 1L, pendingIntent);
二、原理分析
1、闹钟设置
APP 设置闹钟最终都会调用 AlarmManager 的 setImpl 函数:
private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
String listenerTag, Handler targetHandler, WorkSource workSource,
AlarmClockInfo alarmClock)
参数说明:
| 变量名 | 含义 |
|---|---|
| type | 类型:RTC_WAKEUP、RTC、ELAPSED_REALTIME_WAKEUP、ELAPSED_REALTIME |
| triggerAtMillis | 闹钟触发时间(与 type 对应设置) |
| windowMillis | 窗口时间,精确定间设置 WINDOW_EXACT(0) |
| intervalMillis | 重复闹钟间隔时间 |
| flags | 标记:FLAG_STANDALONE、FLAG_WAKE_FROM_IDLE、FLAG_ALLOW_WHILE_IDLE、FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED、FLAG_IDLE_UNTIL |
| operation | PendingIntent |
| listener | 与 operation 必须二选其一;重复闹钟,只能设置 operation 参数。 |
| listenerTag | 与 listener 配置使用 |
| targetHandler | OnAlarmListener 的 onAlarm() 方法将通过 targetHandler 调用,如果 targetHandler 为 null,则 onAlarm 将在应用程序的 main looper 上调用。 |
setImpl 函数通过 Binder 会调用到 AlarmManagerService 的 set 函数:
public void set(String callingPackage,
int type, long triggerAtTime, long windowLength, long interval, int flags,
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {
final int callingUid = Binder.getCallingUid();
// make sure the caller is not lying about which package should be blamed for
// wakelock time spent in alarm delivery
mAppOps.checkPackage(callingUid, callingPackage);
// Repeating alarms must use PendingIntent, not direct listener
if (interval != 0) {
if (directReceiver != null) {
throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
}
}
if (workSource != null) {
getContext().enforcePermission(
android.Manifest.permission.UPDATE_DEVICE_STATS,
Binder.getCallingPid(), callingUid, "AlarmManager.set");
}
// No incoming callers can request either WAKE_FROM_IDLE or
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
| AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
// Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
// manager when to come out of idle mode, which is only for DeviceIdleController.
if (callingUid != Process.SYSTEM_UID)

本文详细剖析Android应用如何利用AlarmManager设置闹钟,涉及时钟类型选择、PendingIntent与BroadcastReceiver、系统服务调用过程,以及闹钟触发时机和处理机制。深入理解AlarmManager的工作原理和实际操作细节。

2336

被折叠的 条评论
为什么被折叠?



