You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:05:23