在 Android 的所有最新限制之后,如何将闹钟设置为在准确时间安排?
How to set an alarm to be scheduled at an exact time after all the newest restrictions on Android?
注意:我尝试了 Whosebug 上的各种解决方案(示例 here)。请不要在没有使用我在下面编写的测试中检查您找到的解决方案是否有效的情况下关闭它。
背景
应用程序有一个要求,用户将提醒设置为在特定时间安排,因此当应用程序在这个时间被触发时,它会在后台做一些微小的事情(只是一些数据库查询操作),并显示一个简单的通知,告知提醒。
以前我都是用简单的代码设置一些比较具体的时间安排:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
用法:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
问题
我现在已经在新 Android 版本的模拟器和 Android 10 的 Pixel 4 上测试了这段代码,它似乎没有触发,或者可能在很长时间后触发很长一段时间以来我提供它。我很清楚 terrible behavior that some OEMs 添加到从最近的任务中删除应用程序,但这个在模拟器和 Pixel 4 设备(库存)上都有。
我读过 the docs about setting an alarm, that it got restricted for apps so that it won't occur too often, but this doesn't explain how to set an alarm at a specific time, and it doesn't explain how come Google's Clock app 成功了。
不仅如此,据我了解,它说限制应该特别适用于设备的低功率状态,但在我的情况下,我没有这种状态,在设备和在模拟器上。我已将闹钟设置为在大约一分钟后触发。
看到许多闹钟应用程序不再像以前那样工作了,我认为文档中缺少一些东西。此类应用的示例是流行的 Timely app that was bought by Google but never got new updates to handle the new restrictions, and now users want it back.. However, some popular apps do work fine, such as this one.
我试过的
为了测试闹钟是否确实有效,我在第一次安装应用程序后尝试在一分钟后触发闹钟时执行这些测试,同时设备连接到 PC(以查看日志):
- 测试应用何时位于前台,对用户可见。 - 用了 1-2 分钟。
- 测试何时将应用程序发送到后台(例如使用主页按钮)- 大约需要 1 分钟
- 测试何时从最近的任务中删除了应用程序的任务。 - 等了20多分钟也没看到报警被触发,写入日志。
- 和#3 一样,但也要关闭屏幕。可能会更糟...
我试过用下面的东西,都不行:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
以上任何一项的组合,与:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT)
alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
试图使用服务而不是 BroadcastReceiver。还尝试了不同的过程。
尝试让该应用在电池优化中被忽略(没有帮助),但由于其他应用不需要它,我也不应该使用它。
尝试使用这个:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- 尝试使用一个会触发 onTaskRemoved 的服务,重新安排那里的警报,但这也没有帮助(虽然服务工作正常)。
至于 Google 的时钟应用程序,我没有看到它有什么特别之处,只是它在被触发之前显示了一个通知,而且我在 [=130= 中也没有看到它] 电池优化设置屏幕部分。
看到这似乎是一个错误,我报告了这个 here,包括一个示例项目和视频来展示这个问题。
我检查了多个版本的模拟器,似乎这种行为是从 API 27 (Android 8.1 - Oreo) 开始的。查看 at the docs,我没有看到 AlarmManager 被提及,而是写了各种后台工作。
问题
现在我们如何设置在相对准确的时间触发?
为什么上述解决方案不再有效?我错过了什么吗?允许?也许我应该改用工人?但是那岂不是意味着它可能根本不会按时触发?
Google"Clock" 应用程序如何克服所有这些问题,并始终在准确的时间触发,即使它是在一分钟前触发的?仅仅是因为它是一个系统应用程序吗?如果它作为用户应用安装在没有内置它的设备上怎么办?
如果你说这是因为它是一个系统应用程序,我发现另一个应用程序可以在 2 分钟内触发两次警报,here,虽然我认为它有时可能会使用前台服务。
编辑:制作了一个小型 Github 存储库来尝试想法,here。
编辑:最终 found a sample 既是开源的又没有这个问题。可悲的是,它非常复杂,我仍然试图弄清楚是什么让它如此不同(以及我应该添加到我的 POC 中的最少代码是什么),以便在从最近的任务中删除该应用程序后,它的警报仍保持预定
- 确保您广播的意图是明确的并且具有
Intent.FLAG_RECEIVER_FOREGROUND
标志。
https://developer.android.com/about/versions/oreo/background#broadcasts
Intent intent = new Intent(context, Receiver.class);
intent.setAction(action);
...
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent operation = PendingIntent.getBroadcast(context, 0, intent, flags);
- 定位 API 23+ 时使用
setExactAndAllowWhileIdle()
。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, operation);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, operation);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, time, operation);
}
- 将闹钟作为前台服务启动:
https://developer.android.com/about/versions/oreo/background#migration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
- 不要忘记权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
我们没事做。
一旦您的应用程序未列入白名单,一旦从最近的应用程序中删除,它将始终被终止。
因为Original Equipment Manufacturer (OMEs) constantly violating Android compliance.
因此,如果您的应用未被设备制造商列入白名单,它不会触发任何后台工作even alarms - 以防您的应用从最近的应用中删除。
您可以找到具有该行为的设备列表here您也可能会找到一个附带的解决方案,但是,它不会很好地工作。
找到了一个奇怪的解决方法(示例 here)似乎适用于所有版本,甚至包括 Android R:
- 具有清单中声明的 SAW 权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
在 Android R 上,您还必须授予它。之前,好像不需要授予,只是声明。不知道为什么这在 R 上发生了变化,但我可以说可能需要 SAW 作为在后台启动事物的可能解决方案,如 here 为 Android 10.
所写
编辑:这里是 guide 如何请求它。
- 有一个服务可以检测任务何时被删除,当它删除时,打开一个假的 Activity 它所做的只是关闭自己:
class OnTaskRemovedDetectorService : Service() {
override fun onBind(intent: Intent?) = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.e("AppLog", "onTaskRemoved")
applicationContext.startActivity(Intent(this, FakeActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
stopSelf()
}
}
FakeActivity.kt
class FakeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("AppLog", "FakeActivity")
finish()
}
}
您还可以使 Activity 对使用此主题的用户几乎不可见:
<style name="AppTheme.Translucent" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
遗憾的是,这是一个奇怪的解决方法。我希望找到一个更好的解决方法。
关于启动的限制Activity,所以我目前的想法是,如果我启动前台服务一秒钟,它也会有所帮助,为此我什至不需要 SAW许可。
编辑:好的,我尝试使用前台服务(示例 here),但它没有用。不知道为什么 Activity 工作但不是服务。我什至尝试在那里重新安排警报,并试图让服务停留一段时间,即使在重新安排之后也是如此。还尝试了一个正常的服务,但当然它立即关闭,因为任务被删除了,它根本不起作用(即使我在后台创建了一个到 运行 的线程)。
我没有尝试过的另一种可能的解决方案是永远拥有前台服务,或者至少在任务被删除之前,但这有点奇怪,我没有看到我提到的应用程序使用它。
编辑:尝试在删除应用程序任务之前 运行ning 有一个前台服务,之后一段时间,警报仍然有效。还尝试让此服务成为负责任务删除事件的服务,并在它发生时立即自行关闭,并且它仍然有效(示例 here)。此变通办法的优点是您根本不必拥有 SAW 许可。缺点是您有一个带有通知的服务,而该应用程序已经对用户可见。我想知道是否可以通过 Activity.
在应用程序已经在前台时隐藏通知
编辑:这似乎是 Android Studio 上的错误(已报告 here,包括比较版本的视频)。
当您从我试过的有问题的版本启动应用程序时,它可能会导致警报被清除。
如果您从启动器启动该应用程序,它工作正常。
这是设置闹钟的当前代码:
val timeToTrigger = System.currentTimeMillis() + 10 * 1000
val pendingShowList = PendingIntent.getActivity(this, 1, Intent(this, SomeActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
val pendingIntent = PendingIntent.getBroadcast(this, 1, Intent(this, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
manager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingShowList), pendingIntent)
我什至不必使用“pendingShowList”。使用null也可以。
我知道这效率不高,但可能更符合 60 秒的准确度。
https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK
如果这个广播接收器在前台服务中使用,你可以每分钟检查一次时间并决定采取什么行动。
我觉得你可以要求用户设置权限,关闭节能模式,并警告用户,如果他不使用,将无法达到准确的时间。
这是请求它的代码:
PowerManager powerManager = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
String packageName = "your Package name";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent i = new Intent();
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
i.setData(Uri.parse("package:" + packageName));
startActivity(i);
我是你问题中提到的开源项目的作者(simple alarm clock)。
我很惊讶使用 AlarmManager.setAlarmClock 对您不起作用,因为我的应用程序正是这样做的。代码在文件 AlarmSetter.kt 中。这是一个片段:
val pendingAlarm = Intent(ACTION_FIRED)
.apply {
setClass(mContext, AlarmsReceiver::class.java)
putExtra(EXTRA_ID, id)
putExtra(EXTRA_TYPE, typeName)
}
.let { PendingIntent.getBroadcast(mContext, pendingAlarmRequestCode, it, PendingIntent.FLAG_UPDATE_CURRENT) }
val pendingShowList = PendingIntent.getActivity(
mContext,
100500,
Intent(mContext, AlarmsListActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
am.setAlarmClock(AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingShowList), pendingAlarm)
基本上没什么特别的,只要确保意图有一个动作和一个目标class,在我的例子中是一个广播接收器。
实际上,对我来说 Android 12 安排准确时间闹钟的方法是向您应用的 AndroidManifest 文件添加权限:
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
并为您的 PendingIntent 更改标志:
val pendingIntent = alarmIntent.let { intent ->
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE //this is needed in Android 12
} else {
PendingIntent.FLAG_CANCEL_CURRENT
}
PendingIntent.getBroadcast(
context,
DailyAlarmReceiver.REQUEST_DAILY_NOTIFICATION,
intent,
flag
)
}
您还可以通过检查 canScheduleExactAlarms:
来检查该应用是否可以安排闹钟
val alarmManager: AlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if(alarmManager.canScheduleExactAlarms()){
//schedule the alarm
}
注意:我尝试了 Whosebug 上的各种解决方案(示例 here)。请不要在没有使用我在下面编写的测试中检查您找到的解决方案是否有效的情况下关闭它。
背景
应用程序有一个要求,用户将提醒设置为在特定时间安排,因此当应用程序在这个时间被触发时,它会在后台做一些微小的事情(只是一些数据库查询操作),并显示一个简单的通知,告知提醒。
以前我都是用简单的代码设置一些比较具体的时间安排:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
用法:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
问题
我现在已经在新 Android 版本的模拟器和 Android 10 的 Pixel 4 上测试了这段代码,它似乎没有触发,或者可能在很长时间后触发很长一段时间以来我提供它。我很清楚 terrible behavior that some OEMs 添加到从最近的任务中删除应用程序,但这个在模拟器和 Pixel 4 设备(库存)上都有。
我读过 the docs about setting an alarm, that it got restricted for apps so that it won't occur too often, but this doesn't explain how to set an alarm at a specific time, and it doesn't explain how come Google's Clock app 成功了。
不仅如此,据我了解,它说限制应该特别适用于设备的低功率状态,但在我的情况下,我没有这种状态,在设备和在模拟器上。我已将闹钟设置为在大约一分钟后触发。
看到许多闹钟应用程序不再像以前那样工作了,我认为文档中缺少一些东西。此类应用的示例是流行的 Timely app that was bought by Google but never got new updates to handle the new restrictions, and now users want it back.. However, some popular apps do work fine, such as this one.
我试过的
为了测试闹钟是否确实有效,我在第一次安装应用程序后尝试在一分钟后触发闹钟时执行这些测试,同时设备连接到 PC(以查看日志):
- 测试应用何时位于前台,对用户可见。 - 用了 1-2 分钟。
- 测试何时将应用程序发送到后台(例如使用主页按钮)- 大约需要 1 分钟
- 测试何时从最近的任务中删除了应用程序的任务。 - 等了20多分钟也没看到报警被触发,写入日志。
- 和#3 一样,但也要关闭屏幕。可能会更糟...
我试过用下面的东西,都不行:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
以上任何一项的组合,与:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
试图使用服务而不是 BroadcastReceiver。还尝试了不同的过程。
尝试让该应用在电池优化中被忽略(没有帮助),但由于其他应用不需要它,我也不应该使用它。
尝试使用这个:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- 尝试使用一个会触发 onTaskRemoved 的服务,重新安排那里的警报,但这也没有帮助(虽然服务工作正常)。
至于 Google 的时钟应用程序,我没有看到它有什么特别之处,只是它在被触发之前显示了一个通知,而且我在 [=130= 中也没有看到它] 电池优化设置屏幕部分。
看到这似乎是一个错误,我报告了这个 here,包括一个示例项目和视频来展示这个问题。
我检查了多个版本的模拟器,似乎这种行为是从 API 27 (Android 8.1 - Oreo) 开始的。查看 at the docs,我没有看到 AlarmManager 被提及,而是写了各种后台工作。
问题
现在我们如何设置在相对准确的时间触发?
为什么上述解决方案不再有效?我错过了什么吗?允许?也许我应该改用工人?但是那岂不是意味着它可能根本不会按时触发?
Google"Clock" 应用程序如何克服所有这些问题,并始终在准确的时间触发,即使它是在一分钟前触发的?仅仅是因为它是一个系统应用程序吗?如果它作为用户应用安装在没有内置它的设备上怎么办?
如果你说这是因为它是一个系统应用程序,我发现另一个应用程序可以在 2 分钟内触发两次警报,here,虽然我认为它有时可能会使用前台服务。
编辑:制作了一个小型 Github 存储库来尝试想法,here。
编辑:最终 found a sample 既是开源的又没有这个问题。可悲的是,它非常复杂,我仍然试图弄清楚是什么让它如此不同(以及我应该添加到我的 POC 中的最少代码是什么),以便在从最近的任务中删除该应用程序后,它的警报仍保持预定
- 确保您广播的意图是明确的并且具有
Intent.FLAG_RECEIVER_FOREGROUND
标志。
https://developer.android.com/about/versions/oreo/background#broadcasts
Intent intent = new Intent(context, Receiver.class);
intent.setAction(action);
...
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent operation = PendingIntent.getBroadcast(context, 0, intent, flags);
- 定位 API 23+ 时使用
setExactAndAllowWhileIdle()
。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, operation);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, operation);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, time, operation);
}
- 将闹钟作为前台服务启动:
https://developer.android.com/about/versions/oreo/background#migration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
- 不要忘记权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
我们没事做。
一旦您的应用程序未列入白名单,一旦从最近的应用程序中删除,它将始终被终止。
因为Original Equipment Manufacturer (OMEs) constantly violating Android compliance.
因此,如果您的应用未被设备制造商列入白名单,它不会触发任何后台工作even alarms - 以防您的应用从最近的应用中删除。
您可以找到具有该行为的设备列表here您也可能会找到一个附带的解决方案,但是,它不会很好地工作。
找到了一个奇怪的解决方法(示例 here)似乎适用于所有版本,甚至包括 Android R:
- 具有清单中声明的 SAW 权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
在 Android R 上,您还必须授予它。之前,好像不需要授予,只是声明。不知道为什么这在 R 上发生了变化,但我可以说可能需要 SAW 作为在后台启动事物的可能解决方案,如 here 为 Android 10.
所写编辑:这里是 guide 如何请求它。
- 有一个服务可以检测任务何时被删除,当它删除时,打开一个假的 Activity 它所做的只是关闭自己:
class OnTaskRemovedDetectorService : Service() {
override fun onBind(intent: Intent?) = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.e("AppLog", "onTaskRemoved")
applicationContext.startActivity(Intent(this, FakeActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
stopSelf()
}
}
FakeActivity.kt
class FakeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("AppLog", "FakeActivity")
finish()
}
}
您还可以使 Activity 对使用此主题的用户几乎不可见:
<style name="AppTheme.Translucent" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
遗憾的是,这是一个奇怪的解决方法。我希望找到一个更好的解决方法。
关于启动的限制Activity,所以我目前的想法是,如果我启动前台服务一秒钟,它也会有所帮助,为此我什至不需要 SAW许可。
编辑:好的,我尝试使用前台服务(示例 here),但它没有用。不知道为什么 Activity 工作但不是服务。我什至尝试在那里重新安排警报,并试图让服务停留一段时间,即使在重新安排之后也是如此。还尝试了一个正常的服务,但当然它立即关闭,因为任务被删除了,它根本不起作用(即使我在后台创建了一个到 运行 的线程)。
我没有尝试过的另一种可能的解决方案是永远拥有前台服务,或者至少在任务被删除之前,但这有点奇怪,我没有看到我提到的应用程序使用它。
编辑:尝试在删除应用程序任务之前 运行ning 有一个前台服务,之后一段时间,警报仍然有效。还尝试让此服务成为负责任务删除事件的服务,并在它发生时立即自行关闭,并且它仍然有效(示例 here)。此变通办法的优点是您根本不必拥有 SAW 许可。缺点是您有一个带有通知的服务,而该应用程序已经对用户可见。我想知道是否可以通过 Activity.
在应用程序已经在前台时隐藏通知编辑:这似乎是 Android Studio 上的错误(已报告 here,包括比较版本的视频)。 当您从我试过的有问题的版本启动应用程序时,它可能会导致警报被清除。
如果您从启动器启动该应用程序,它工作正常。
这是设置闹钟的当前代码:
val timeToTrigger = System.currentTimeMillis() + 10 * 1000
val pendingShowList = PendingIntent.getActivity(this, 1, Intent(this, SomeActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
val pendingIntent = PendingIntent.getBroadcast(this, 1, Intent(this, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
manager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingShowList), pendingIntent)
我什至不必使用“pendingShowList”。使用null也可以。
我知道这效率不高,但可能更符合 60 秒的准确度。
https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK
如果这个广播接收器在前台服务中使用,你可以每分钟检查一次时间并决定采取什么行动。
我觉得你可以要求用户设置权限,关闭节能模式,并警告用户,如果他不使用,将无法达到准确的时间。
这是请求它的代码:
PowerManager powerManager = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
String packageName = "your Package name";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent i = new Intent();
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
i.setData(Uri.parse("package:" + packageName));
startActivity(i);
我是你问题中提到的开源项目的作者(simple alarm clock)。
我很惊讶使用 AlarmManager.setAlarmClock 对您不起作用,因为我的应用程序正是这样做的。代码在文件 AlarmSetter.kt 中。这是一个片段:
val pendingAlarm = Intent(ACTION_FIRED)
.apply {
setClass(mContext, AlarmsReceiver::class.java)
putExtra(EXTRA_ID, id)
putExtra(EXTRA_TYPE, typeName)
}
.let { PendingIntent.getBroadcast(mContext, pendingAlarmRequestCode, it, PendingIntent.FLAG_UPDATE_CURRENT) }
val pendingShowList = PendingIntent.getActivity(
mContext,
100500,
Intent(mContext, AlarmsListActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
am.setAlarmClock(AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingShowList), pendingAlarm)
基本上没什么特别的,只要确保意图有一个动作和一个目标class,在我的例子中是一个广播接收器。
实际上,对我来说 Android 12 安排准确时间闹钟的方法是向您应用的 AndroidManifest 文件添加权限:
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
并为您的 PendingIntent 更改标志:
val pendingIntent = alarmIntent.let { intent ->
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE //this is needed in Android 12
} else {
PendingIntent.FLAG_CANCEL_CURRENT
}
PendingIntent.getBroadcast(
context,
DailyAlarmReceiver.REQUEST_DAILY_NOTIFICATION,
intent,
flag
)
}
您还可以通过检查 canScheduleExactAlarms:
来检查该应用是否可以安排闹钟val alarmManager: AlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if(alarmManager.canScheduleExactAlarms()){
//schedule the alarm
}