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
相关产品推荐
相关产品推荐

