You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android:如何构建满足特定重复规则的Periodic WorkManager以定时发送通知?

用WorkManager实现定时重复通知方案

嘿,我来帮你搞定这两个定时通知需求!WorkManager确实是Android上处理这类后台重复任务的最佳选择之一,不过针对每周固定时间每月固定日期且限次这两种不同规则,实现方式略有区别,咱们一步步来拆解:

1. 基础准备:创建Worker类与通知渠道

首先,我们需要一个核心的Worker类来处理通知发送逻辑,同时要记得为Android 8.0+创建通知渠道(否则通知发不出来)。

1.1 通知渠道初始化(在Application或启动页完成)

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "REMINDER_CHANNEL",
                "定时提醒频道",
                NotificationManager.IMPORTANCE_DEFAULT
            ).apply {
                description = "用于发送每周/每月的定时提醒通知"
            }
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }
    }
}

1.2 核心Worker类实现

这个Worker既处理每周通知,也处理每月通知,通过输入数据区分任务类型,同时负责每月任务的后续调度(直到满6次):

class NotificationWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        // 获取任务类型和当前执行次数
        val taskType = inputData.getString("TASK_TYPE") ?: ""
        val currentCount = inputData.getInt("EXECUTION_COUNT", 1)

        // 发送对应类型的通知
        sendReminderNotification(taskType)

        // 每月任务专属:如果还没到6次,调度下一次
        if (taskType == "MONTHLY" && currentCount < 6) {
            scheduleNextMonthlyTask(currentCount + 1)
        }

        return Result.success()
    }

    private fun sendReminderNotification(taskType: String) {
        val (title, content) = when(taskType) {
            "WEEKLY" -> "每周周二提醒" to "今天是周二14:30,别忘了你的待办哦!"
            "MONTHLY" -> "每月15号提醒" to "今天是15号,月度提醒已送达!"
            else -> "定时提醒" to "你的定时任务已触发"
        }

        val notification = NotificationCompat.Builder(applicationContext, "REMINDER_CHANNEL")
            .setSmallIcon(R.drawable.ic_notification) // 替换成你的通知图标
            .setContentTitle(title)
            .setContentText(content)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .build()

        val notificationManager = applicationContext.getSystemService(NotificationManager::class.java)
        notificationManager.notify(System.currentTimeMillis().toInt(), notification)
    }

    private fun scheduleNextMonthlyTask(nextCount: Int) {
        // 计算下一个15号14:30的时间戳
        val targetCalendar = Calendar.getInstance().apply {
            set(Calendar.HOUR_OF_DAY, 14)
            set(Calendar.MINUTE, 30)
            set(Calendar.SECOND, 0)
            set(Calendar.MILLISECOND, 0)

            // 判断当前时间是否已过本月15号14:30,是的话直接跳到下个月
            val isPastTarget = when {
                get(Calendar.DAY_OF_MONTH) > 15 -> true
                get(Calendar.DAY_OF_MONTH) == 15 -> get(Calendar.HOUR_OF_DAY) > 14 || 
                    (get(Calendar.HOUR_OF_DAY) == 14 && get(Calendar.MINUTE) >= 30)
                else -> false
            }
            if (isPastTarget) {
                add(Calendar.MONTH, 1)
            }
            set(Calendar.DAY_OF_MONTH, 15)
        }

        val delayMillis = targetCalendar.timeInMillis - System.currentTimeMillis()

        // 构建下一次的单次任务请求
        val nextWorkRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
            .setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
            .setInputData(workDataOf(
                "TASK_TYPE" to "MONTHLY",
                "EXECUTION_COUNT" to nextCount
            ))
            .build()

        WorkManager.getInstance(applicationContext).enqueue(nextWorkRequest)
    }
}

2. 实现每周二14:30的重复通知

每周任务是固定间隔(7天)的重复任务,我们用PeriodicWorkRequest来实现,关键是计算第一次执行的初始延迟,确保第一次在周二14:30触发:

fun scheduleWeeklyReminder(context: Context) {
    val targetCalendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, 14)
        set(Calendar.MINUTE, 30)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)

        // 找到下一个周二
        while (get(Calendar.DAY_OF_WEEK) != Calendar.TUESDAY) {
            add(Calendar.DAY_OF_MONTH, 1)
        }

        // 如果今天就是周二,但已经过了14:30,就推迟到下周二
        if (System.currentTimeMillis() > timeInMillis) {
            add(Calendar.DAY_OF_MONTH, 7)
        }
    }

    val initialDelay = targetCalendar.timeInMillis - System.currentTimeMillis()

    // 构建周期性任务,间隔7天
    val weeklyWorkRequest = PeriodicWorkRequestBuilder<NotificationWorker>(7, TimeUnit.DAYS)
        .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
        .setInputData(workDataOf("TASK_TYPE" to "WEEKLY"))
        .build()

    // 用唯一名称确保不会重复创建任务
    WorkManager.getInstance(context).enqueueUniquePeriodicWork(
        "WEEKLY_REMINDER_TASK",
        ExistingPeriodicWorkPolicy.REPLACE, // 如果已有相同任务,替换掉
        weeklyWorkRequest
    )
}

3. 实现每月15号(仅执行6次)的重复通知

WorkManager没有直接支持每月重复的API(因为每月天数不固定),所以我们用OneTimeWorkRequest链式调度:每次执行完后,自动调度下一次,直到满6次为止。

3.1 启动第一个每月任务

fun scheduleFirstMonthlyReminder(context: Context) {
    val targetCalendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, 14)
        set(Calendar.MINUTE, 30)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)

        // 判断是否已过本月15号14:30
        val isPastTarget = when {
            get(Calendar.DAY_OF_MONTH) > 15 -> true
            get(Calendar.DAY_OF_MONTH) == 15 -> get(Calendar.HOUR_OF_DAY) > 14 || 
                (get(Calendar.HOUR_OF_DAY) == 14 && get(Calendar.MINUTE) >= 30)
            else -> false
        }
        if (isPastTarget) {
            add(Calendar.MONTH, 1)
        }
        set(Calendar.DAY_OF_MONTH, 15)
    }

    val initialDelay = targetCalendar.timeInMillis - System.currentTimeMillis()

    val firstMonthlyRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
        .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
        .setInputData(workDataOf(
            "TASK_TYPE" to "MONTHLY",
            "EXECUTION_COUNT" to 1
        ))
        .build()

    WorkManager.getInstance(context).enqueue(firstMonthlyRequest)
}

关键注意事项

  • 权限问题:Android 13及以上需要申请POST_NOTIFICATIONS权限,记得在Manifest中声明,并在运行时请求用户授权。
  • 后台限制:Android 12+对后台任务有严格限制,WorkManager会尽量保证任务执行,但如果设备处于深度休眠,可能会延迟。如果需要更精确的触发,可以考虑结合AlarmManager(但需要处理Doze模式)。
  • 任务唯一性:每周任务用enqueueUniquePeriodicWork避免重复创建;每月任务只需调用一次scheduleFirstMonthlyReminder,后续由Worker自动调度。
  • 测试技巧:开发时可以把初始延迟改成几秒(比如setInitialDelay(5, TimeUnit.SECONDS)),快速验证任务是否正常执行。

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

火山引擎 最新活动