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

Android中如何在每日重复通知中展示动态随机单词及翻译并向AlarmReceiver传递参数

解决方案:在每日通知中动态展示随机单词

你的核心问题是需要在每次闹钟触发时获取最新的随机单词,而不是提前把单词传递给AlarmReceiver——因为重复闹钟的PendingIntent如果不重新创建,每次触发时携带的都是最初的数据,根本没法实现每日更新的需求。下面是具体的落地步骤:


1. 重构数据获取逻辑:把随机单词方法移到Repository

ViewModel依赖于Fragment/Activity的生命周期,BroadcastReceiver是完全独立的组件,没法直接用ViewModel。所以得把getRandomWordForNotification()的查询逻辑迁移到Repository(或者一个独立的数据源类)里,让Receiver能直接调用:

// 假设你的Repository类是DictionaryRepository
class DictionaryRepository(private val wordDao: WordDao) {
    // 挂起函数,专门用于从数据库获取随机单词
    suspend fun getRandomWordForNotification(): Word? {
        // 这里写你的数据库查询逻辑,比如:
        return wordDao.getRandomWord()
    }
}

如果用Hilt做依赖注入,记得给Repository加上@Inject注解,方便后续在Receiver里获取实例。


2. 修改AlarmReceiver:触发时实时获取单词并构建通知

在AlarmReceiver的onReceive方法里,我们要做这几件事:

  • 获取Repository实例
  • 在后台线程执行数据库查询(不能卡主线程)
  • 查询完成后回到主线程构建并展示通知
class AlarmReceiver : BroadcastReceiver() {
    // 用Hilt的EntryPoint来获取Repository实例
    @InstallIn(SingletonComponent::class)
    interface AlarmReceiverEntryPoint {
        fun getDictionaryRepository(): DictionaryRepository
    }

    override fun onReceive(context: Context, intent: Intent) {
        // 从Application中获取Hilt EntryPoint
        val entryPoint = EntryPointAccessors.fromApplication(
            context.applicationContext,
            AlarmReceiverEntryPoint::class.java
        )
        val repository = entryPoint.getDictionaryRepository()

        // 用协程在后台执行数据库操作
        CoroutineScope(Dispatchers.IO).launch {
            val randomWord = repository.getRandomWordForNotification()

            // 切回主线程构建并显示通知
            withContext(Dispatchers.Main) {
                randomWord?.let { word ->
                    showNotification(context, word.word, word.translation)
                } ?: run {
                    // 数据库里没数据时显示默认内容
                    showNotification(context, "暂无可用单词", "-")
                }
            }
        }
    }

    private fun showNotification(context: Context, word: String, translation: String) {
        // 创建通知点击后的跳转Intent
        val launchIntent = Intent(context, ListFragment::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            launchIntent,
            // 适配Android 12+的Flag,同时允许更新Intent内容
            PendingIntent.FLAG_UPDATE_CURRENT or if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
        )

        // 构建通知内容
        val notification = NotificationCompat.Builder(context, "channelID")
            .setSmallIcon(R.drawable.ic_add_notification)
            .setContentTitle("每日单词提醒")
            .setContentText("$word - $translation")
            .setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .build()

        NotificationManagerCompat.from(context).notify(123, notification)
    }
}

3. 优化ListFragment中的闹钟设置

注意设置PendingIntent时的Flag,还要处理闹钟时间已经过去的情况(比如现在是上午10点,你设置的是上午8点,那就应该让闹钟明天8点再触发):

private fun setAlarm(){
    alarmManager = context?.getSystemService(ALARM_SERVICE) as AlarmManager
    val intent = Intent(context, AlarmReceiver::class.java)
    val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    } else {
        PendingIntent.FLAG_UPDATE_CURRENT
    }
    pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags)
    
    calendar.set(Calendar.HOUR_OF_DAY, HOUR_FOR_NOTIFICATION)
    calendar.set(Calendar.MINUTE, 0)
    calendar.set(Calendar.SECOND, 0)
    calendar.set(Calendar.MILLISECOND, 0)

    // 如果设置的时间已经在当前时间之前,就把日期加1,避免立即触发
    if (calendar.timeInMillis < System.currentTimeMillis()) {
        calendar.add(Calendar.DAY_OF_YEAR, 1)
    }

    alarmManager?.setRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        AlarmManager.INTERVAL_DAY,
        pendingIntent
    )
}

关键细节说明

  • 为什么不能提前传参数?:setRepeating创建的重复闹钟会一直复用同一个PendingIntent,除非你重新创建PendingIntent并传入新数据,否则每次触发时Intent里的参数都是最初设置的。而我们需要的是每次触发都获取最新的随机单词,所以直接在Receiver里实时查询数据库是最可靠的方案。
  • 线程处理:数据库操作必须在后台线程执行,所以用协程的Dispatchers.IO;而构建通知需要在主线程,所以切换到Dispatchers.Main。
  • 非Hilt场景的替代方案:如果不用Hilt,你可以通过自定义Application类获取Repository实例,或者直接创建数据库实例(不过还是推荐依赖注入的方式,代码更解耦、易维护)。

内容的提问来源于stack exchange,提问作者apomytkina

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 11:19:08