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

Android使用WorkManager实现整点通知及调度、显示效果问题求解

Android整点推送通知功能修复方案

核心问题修复逻辑

  • 触发时间偏移:原代码未对齐整点计算首次触发延迟,且错误使用固定flex参数,新增小时级整点延迟计算逻辑,确保首次任务在最近的整点触发,后续每1小时重复执行。
  • 调度被覆盖:原代码每次启动都替换已有调度任务,将ExistingPeriodicWorkPolicy从REPLACE改为KEEP,避免重复创建覆盖原有计时。
  • 全局变暗效果:新增半透明Activity配置Window的dim属性,通知的全屏intent指向该Activity即可实现背景半黑效果。

修正代码

Application类

import android.app.Application
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import com.io.notification.WorkInTime
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

@HiltAndroidApp
class Application : Application(), Configuration.Provider {

    @Inject
    lateinit var workerFactory: HiltWorkerFactory

    override fun getWorkManagerConfiguration(): Configuration =
        Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()

    override fun onCreate() {
        super.onCreate()
        // 启动整点调度
        WorkInTime().runEveryHour(this.applicationContext)
    }
}

调度实现类

import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.Calendar
import java.util.concurrent.TimeUnit

class WorkInTime {

     fun runEveryDay(hourOfTheDay: Int, context: Context) {
        val repeatInterval = 1 // 单位天
        val flexTime: Long = calculateFlex(hourOfTheDay, repeatInterval)
        schedule(repeatInterval, flexTime, context, TimeUnit.DAYS)
    }

    fun runEveryHour(context: Context){
        val repeatInterval = 1 // 单位小时
        // 计算到下一个整点的延迟
        val initialDelay = calculateNextHourDelay()
        val workRequest = PeriodicWorkRequestBuilder<NotifyWork>(
            repeatInterval.toLong(), TimeUnit.HOURS
        )
            .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
            .setConstraints(Constraints.Builder().setRequiresBatteryNotLow(false).build())
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "YOUR_NICE_WORK_TAG",
            ExistingPeriodicWorkPolicy.KEEP, // 改为KEEP避免覆盖已有调度
            workRequest
        )
    }

    private fun schedule(repeatInterval: Int, flexTime: Long, context: Context, timeUnit: TimeUnit) {
        val myConstraints: Constraints = Constraints.Builder()
            .setRequiresBatteryNotLow(false)
            .build()

        val workRequest = PeriodicWorkRequestBuilder<NotifyWork>(
            repeatInterval.toLong(), timeUnit,
            flexTime, TimeUnit.MILLISECONDS
        )
            .setConstraints(myConstraints)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "YOUR_NICE_WORK_TAG",
            ExistingPeriodicWorkPolicy.KEEP, // 日度任务也改为KEEP
            workRequest
        )
    }

    // 新增计算下一个整点的延迟毫秒数
    private fun calculateNextHourDelay(): Long {
        val currentTime = Calendar.getInstance()
        val nextHour = Calendar.getInstance().apply {
            set(Calendar.MINUTE, 0)
            set(Calendar.SECOND, 0)
            set(Calendar.MILLISECOND, 0)
            add(Calendar.HOUR_OF_DAY, 1)
        }
        val delay = nextHour.timeInMillis - currentTime.timeInMillis
        return if (delay > 0) delay else TimeUnit.HOURS.toMillis(1)
    }

    // 原有日度flex计算方法保留
    private fun calculateFlex(hourOfTheDay: Int, periodInDays: Int): Long {
        val cal1 = Calendar.getInstance()
        cal1[Calendar.HOUR_OF_DAY] = hourOfTheDay
        cal1[Calendar.MINUTE] = 0
        cal1[Calendar.SECOND] = 0
        cal1[Calendar.MILLISECOND] = 0

        val cal2 = Calendar.getInstance()
        if (cal2.timeInMillis < cal1.timeInMillis) {
            cal2.timeInMillis = cal2.timeInMillis + TimeUnit.DAYS.toMillis(periodInDays.toLong())
        }
        val delta = cal2.timeInMillis - cal1.timeInMillis
        return if (delta > androidx.work.PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS) delta else androidx.work.PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS
    }
}

变暗效果实现

1. 新增Activity主题(res/values/styles.xml)

<style name="TransparentDimTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:backgroundDimEnabled">true</item>
    <item name="android:backgroundDimAmount">0.7</item> <!-- 调整这个值控制变暗程度,0-1范围 -->
</style>

2. 新增DimNotificationActivity.kt

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class DimNotificationActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 这里可以设置你的通知展示布局,也可以直接关闭跳转到主页面
        // 如果需要展示通知内容就setContentView,不需要的话可以直接跳转后finish
        val intent = packageManager.getLaunchIntentForPackage(packageName)
        startActivity(intent)
        finish()
    }
}

3. AndroidManifest.xml注册Activity

<activity
    android:name=".DimNotificationActivity"
    android:theme="@style/TransparentDimTheme"
    android:exported="false" />

Worker类修改(调整FullScreenIntent指向)

只需要修改sendNotification方法里的intent指向即可:

private fun sendNotification(id: Int) {
    // 把原来指向MainActivity的intent改成指向DimNotificationActivity
    val intent = Intent(applicationContext, DimNotificationActivity::class.java)
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    intent.putExtra(NOTIFICATION_ID, id)
    // 其余原有代码保持不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 13:45:04