Android AlarmManager - AlarmManager 初识、精确闹钟权限、闹钟覆盖
一、AlarmManager 初识
1、基本介绍
AlarmManager 是 Android 系统提供的全局定时服务,用于在指定时间触发任务
从 Android 4.4(API 19)开始,系统默认将闹钟调整为不精确的,以批量处理任务、减少设备唤醒,从而显著省电
只有在特殊需求时,才应使用精确闹钟,例如,闹钟应用
- 设置闹钟前,需要先确定时间基准,主要如下两种
| 类型 | 说明 |
|---|---|
ELAPSED_REALTIME | 系统启动后经过的时间,适用于相对时间间隔的场景 |
RTC | 真实世界时间,适用于特定时刻触发的场景 |
XXX_WAKEUP类型的闹钟(例如,RTC_WAKEUP)能在设备休眠时唤醒 CPU,非唤醒版本的闹钟要等到设备下次自然唤醒才能执行
2、演示
(1)Receiver
- MyAlarmReceiver.java
publicclassMyAlarmReceiverextendsBroadcastReceiver{publicstaticfinalStringTAG=MyAlarmReceiver.class.getSimpleName();@OverridepublicvoidonReceive(Contextcontext,Intentintent){Log.i(TAG,"触发了!!!");}}- AndroidManifest.xml
<receiverandroid:name=".receiver.MyAlarmReceiver"android:exported="false"/>(2)Activity
ButtonbtnSetAlarm=findViewById(R.id.btn_set_alarm);btnSetAlarm.setOnClickListener(v->{AlarmManageralarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);Intentintent=newIntent(this,MyAlarmReceiver.class);PendingIntentpendingIntent=PendingIntent.getBroadcast(this,0,intent,PendingIntent.FLAG_IMMUTABLE);alarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+5*1000,pendingIntent);});二、精确闹钟权限
1、基本介绍
从 Android 12 开始,使用精确闹钟必须声明权限,否则报错
从 Android 12 开始,调用精确闹钟前,务必使用 canScheduleExactAlarms 方法检查权限
2、演示
- 执行如下代码,Android 10 正常执行,Android 13 报错,错误信息如下
ButtonbtnSetAlarm=findViewById(R.id.btn_set_alarm);btnSetAlarm.setOnClickListener(v->{AlarmManageralarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);Intentintent=newIntent(this,MyAlarmReceiver.class);PendingIntentpendingIntent=PendingIntent.getBroadcast(this,0,intent,PendingIntent.FLAG_IMMUTABLE);alarmManager.setExact(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+5*1000,pendingIntent);});# 输出结果 FATAL EXCEPTION: main Process: com.my.alarmmanager, PID: 4754 java.lang.SecurityException: Caller com.my.alarmmanager needs to hold android.permission.SCHEDULE_EXACT_ALARM or android.permission.USE_EXACT_ALARM to set exact alarms.- Android 13 执行如下代码,检查权限
AlarmManageralarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.S){Log.i(TAG,"Android 12 及以上");booleanresult=alarmManager.canScheduleExactAlarms();Log.i(TAG,"result: "+result);}else{Log.i(TAG,"Android 12 以下");}# 输出结果 Android 12 及以上 result: false3、请求权限
- 在 AndroidManifest.xml 文件中声明权限
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />- 检查权限与引导授权
publicclassMainActivityextendsAppCompatActivity{publicstaticfinalStringTAG=MainActivity.class.getSimpleName();privateAlarmManageralarmManager;@SuppressWarnings("all")@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main),(v,insets)->{InsetssystemBars=insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left,systemBars.top,systemBars.right,systemBars.bottom);returninsets;});alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);if(checkExactAlarmPermission()){next();}else{ActivityResultLauncher<Intent>intentActivityResultLauncher=registerForActivityResult(newActivityResultContracts.StartActivityForResult(),o->{booleanresult=alarmManager.canScheduleExactAlarms();if(result){next();}else{Toast.makeText(this,"未获取到相关权限,无法使用本功能",Toast.LENGTH_SHORT).show();finish();}});Intentintent=newIntent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);intentActivityResultLauncher.launch(intent);}}privatebooleancheckExactAlarmPermission(){if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.S){Log.i(TAG,"Android 12 及以上");booleanresult=alarmManager.canScheduleExactAlarms();Log.i(TAG,"result: "+result);returnresult;}else{Log.i(TAG,"Android 12 以下");returntrue;}}privatevoidnext(){ButtonbtnSetAlarm=findViewById(R.id.btn_set_alarm);btnSetAlarm.setOnClickListener(v->{AlarmManageralarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);Intentintent=newIntent(this,MyAlarmReceiver.class);PendingIntentpendingIntent=PendingIntent.getBroadcast(this,0,intent,PendingIntent.FLAG_IMMUTABLE);alarmManager.setExact(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+5*1000,pendingIntent);});}}三、闹钟覆盖
1、演示
(1)Receiver
- TestAlarmReceiver.java
publicclassTestAlarmReceiverextendsBroadcastReceiver{publicstaticfinalStringTAG=TestAlarmReceiver.class.getSimpleName();@SuppressWarnings("all")@OverridepublicvoidonReceive(Contextcontext,Intentintent){LocalDateTimedateTime=LocalDateTime.now();DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");Stringstr=dateTime.format(formatter);Log.i(TAG,"onReceive: "+str);Log.i(TAG,"触发了!!!");}}- AndroidManifest.xml
<receiverandroid:name=".receiver.TestAlarmReceiver"android:exported="false"/>(2)Activity
ButtonbtnSetAlarm=findViewById(R.id.btn_set_alarm);btnSetAlarm.setOnClickListener(v->{LocalDateTimedateTime=LocalDateTime.now();DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");Stringstr=dateTime.format(formatter);Log.i(TAG,"btnSetAlarm click: "+str);AlarmManageralarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);Intentintent=newIntent(this,TestAlarmReceiver.class);PendingIntentpendingIntent=PendingIntent.getBroadcast(this,0,intent,PendingIntent.FLAG_IMMUTABLE);alarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+5000,pendingIntent);});(3)Test
- 连续两次按钮,输出如下内容
btnSetAlarm click: 2026-06-15 10:01:53 btnSetAlarm click: 2026-06-15 10:01:56 onReceive: 2026-06-15 10:02:01 触发了!!!2、基本介绍
AlarmManager 判断两个闹钟是否为同一个任务,是通过 PendingIntent 来进行匹配的
两次执行时,requestCode 都是 0,Intent 也都是指向
TestAlarmReceiver.class的,因此生成的 PendingIntent 都是相同的第二次设置的闹钟会直接覆盖第一次设置的闹钟,最终系统只会保留并触发第二次设置的闹钟
