AlarmManager未按设定时间执行,随机触发通知问题求助
问题排查与解决方法
问题描述
已配置AlarmManager在每日7点触发,每隔24小时重复执行,预期实现切换ImageView并发送通知。但实际情况是:应用启动或关闭后1-2分钟就会触发通知,且ImageView仅偶尔切换。
代码片段
主活动代码
val mIntent = Intent(this, MyReceiver::class.java) val calendar: Calendar = Calendar.getInstance() calendar.setTimeInMillis(System.currentTimeMillis()) calendar.set(Calendar.HOUR_OF_DAY, 7) calendar.set(Calendar.MINUTE, 0) val mPendingIntent = PendingIntent.getBroadcast(this, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT) val mAlarmManager = this.getSystemService(Context.ALARM_SERVICE) as AlarmManager mAlarmManager.setRepeating( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),86400000, mPendingIntent, )
MyReceiver代码
class MyReceiver : BroadcastReceiver() { @RequiresApi(Build.VERSION_CODES.O) override fun onReceive(context: Context, intent: Intent) { val titles = arrayOf("Become inspired!", "Check out this quote!", "A new quote appeared!", "Daily quote available!") val title = titles.random() val notificationChannel = NotificationChannel("My Channel", "New Quote", NotificationManager.IMPORTANCE_DEFAULT).apply { description = "Alerts when A new daily quote is set!" } val builder = NotificationCompat.Builder(context!!, "My Channel") .setSmallIcon(R.drawable.ic_stat_name) .setContentTitle(title) .setContentText("A new daily quote is available for viewing") with(NotificationManagerCompat.from(context)){ createNotificationChannel(notificationChannel) notify(1, builder.build()) } val quotes = arrayOf(R.drawable.i1, R.drawable.i2, R.drawable.i3, R.drawable.i5, R.drawable.i6, R.drawable.i7, R.drawable.i8, R.drawable.i9, R.drawable.i10, R.drawable.i11, R.drawable.i12) val quote = quotes.random() val prefs = PreferenceManager.getDefaultSharedPreferences(context) with(prefs.edit()) { putInt("paintings", quote) apply() } } }
Other页面代码
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.other) val imageView = findViewById<ImageView>(R.id.paininass) val prefs = PreferenceManager.getDefaultSharedPreferences(this) val quote = prefs.getInt("paintings", R.drawable.i5) imageView.setImageResource(quote) }
问题排查
1. AlarmManager触发时机错误
当前代码中,如果设置的7点早于当前时间(比如当前是上午8点),calendar.getTimeInMillis()会返回当天7点的时间戳——这个时间已经过去,调用setRepeating会立即触发一次闹钟,之后才会按24小时间隔执行。这就是应用启动后很快收到通知的原因。
2. ImageView不自动刷新
Other页面仅在onCreate方法中读取SharedPreferences设置图片,只有当页面被重新创建(比如首次打开、屏幕旋转)时才会更新。如果Alarm触发时页面已经处于前台,不会自动刷新ImageView,导致“仅偶尔切换”的现象。
3. 其他潜在问题
- NotificationChannel重复创建:每次触发广播都会创建一次通道,虽然系统会忽略重复创建,但没必要重复执行。
- 版本兼容性:
@RequiresApi(Build.VERSION_CODES.O)注解会导致低于Android 8.0的设备无法创建通知通道,可能引发通知不显示问题。
解决方法
修复Alarm触发时间
修改主活动中Calendar的设置逻辑,判断当前时间是否已过7点,若已过则将闹钟设置为第二天7点:
val calendar: Calendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 7) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) // 精确到整点,避免秒数差异 // 如果当前时间已超过7点,设置为次日7点 if (timeInMillis < System.currentTimeMillis()) { add(Calendar.DAY_OF_YEAR, 1) } }
优化ImageView刷新逻辑
在Other页面注册SharedPreferences监听器,当存储的图片ID变化时自动更新ImageView:
class OtherActivity : AppCompatActivity() { private lateinit var imageView: ImageView private lateinit var sharedPrefListener: SharedPreferences.OnSharedPreferenceChangeListener override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.other) imageView = findViewById(R.id.paininass) val prefs = PreferenceManager.getDefaultSharedPreferences(this) // 初始化显示图片 updateImageView(prefs) // 注册SharedPreferences监听器 sharedPrefListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> if (key == "paintings") { updateImageView(sharedPreferences) } } prefs.registerOnSharedPreferenceChangeListener(sharedPrefListener) } private fun updateImageView(prefs: SharedPreferences) { val quote = prefs.getInt("paintings", R.drawable.i5) imageView.setImageResource(quote) } override fun onDestroy() { super.onDestroy() // 注销监听器,避免内存泄漏 PreferenceManager.getDefaultSharedPreferences(this) .unregisterOnSharedPreferenceChangeListener(sharedPrefListener) } }
优化通知通道创建逻辑
在MyReceiver中添加版本判断,避免重复创建通知通道:
override fun onReceive(context: Context, intent: Intent) { val titles = arrayOf("Become inspired!", "Check out this quote!", "A new quote appeared!", "Daily quote available!") val title = titles.random() val builder = NotificationCompat.Builder(context, "My Channel") .setSmallIcon(R.drawable.ic_stat_name) .setContentTitle(title) .setContentText("A new daily quote is available for viewing") with(NotificationManagerCompat.from(context)) { // 仅在Android 8.0及以上且通道不存在时创建 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && getNotificationChannel("My Channel") == null) { val notificationChannel = NotificationChannel( "My Channel", "New Quote", NotificationManager.IMPORTANCE_DEFAULT ).apply { description = "Alerts when a new daily quote is set!" } createNotificationChannel(notificationChannel) } notify(1, builder.build()) } // ... 图片ID存储逻辑不变 }
高版本系统Alarm兼容性(可选)
从Android 4.4开始,setRepeating不再保证精确触发,若需要精准的每日闹钟,建议使用setExactAndAllowWhileIdle配合手动重复设置:
// 替换原setRepeating调用 mAlarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, mPendingIntent ) // 在MyReceiver的onReceive最后,重新设置下一次闹钟 val nextCalendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() set(Calendar.HOUR_OF_DAY, 7) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) if (timeInMillis <= System.currentTimeMillis()) { add(Calendar.DAY_OF_YEAR, 1) } } val nextPendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, nextCalendar.timeInMillis, nextPendingIntent )
内容的提问来源于stack exchange,提问作者Joshu
相关产品推荐
相关产品推荐

