Проблема с уведомлениями и периодическими фоновыми задачами на некоторых устройствах (WorkManager + режим Doze)Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Проблема с уведомлениями и периодическими фоновыми задачами на некоторых устройствах (WorkManager + режим Doze)

Сообщение Anonymous »

У меня возникли проблемы с системой уведомлений моего приложения Android и запланированными фоновыми задачами.
На некоторых устройствах (например, Google Pixel 8a) уведомления не приходят, база данных не обновляется автоматически каждые несколько часов или уведомления приходят очень поздно, когда телефон заблокирован или находится в режиме ожидания.
На других устройствах (например, телефонах Samsung) все работает отлично:
  • Уведомления по расписанию доставляются на правильное время.
  • База данных обновляется примерно каждые 4 часа, как и предполагалось.
Контекст приложения
Когда пользователь добавляет аниме, приложение планирует уведомление о времени выпуска следующего эпизода.
У меня также есть задача WorkManager, которая запускается каждые 4 часа, чтобы:
  • Проверить, есть ли время выпуска изменено.
  • При необходимости обновите базу данных Firebase.
  • Соответствующим образом перенесите уведомления.
На некоторых устройствах все работает нормально, но на других (например, Pixel 8a) система, похоже, ограничивает или откладывает фоновые задачи из-за режима ожидания или более агрессивной политики оптимизации заряда батареи.
Вопрос
Есть ли еще надежный способ обеспечить правильную периодическую фоновую работу и запланированные уведомления даже на устройствах с более строгими ограничениями Doze или заряда батареи?
В настоящее время я использую WorkManager.
Следует ли мне рассмотреть альтернативы или дополнительные конфигурации, такие как setExactAndAllowWhileIdle(), ForegroundService или любой другой рекомендуемый подход?
Если возможно, может ли кто-нибудь предоставить базовый пример реализации, показывающий, как:
Запускать надежную фоновую задачу которое выполняется даже в режиме ожидания, и запланировать уведомление, которое будет срабатывать точно в заданное время (даже если устройство находится в режиме ожидания)?
Я покажу, как оно у меня смонтировано:
@HiltAndroidApp
class NotakuApplication : Application(), Configuration.Provider {

@Inject
lateinit var workerFactory: HiltWorkerFactory

override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(Log.ERROR)
.build()

override fun onCreate() {
super.onCreate()
launchPeriodicSyncWorker()
launchImmediateSyncWorker()
}

private fun launchPeriodicSyncWorker() {
val workRequest = PeriodicWorkRequestBuilder(
4, TimeUnit.HOURS
)
.setInitialDelay(1, TimeUnit.MINUTES)
.build()

WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"anime_sync_and_reminder_work",
ExistingPeriodicWorkPolicy.KEEP,
workRequest
)
}

private fun launchImmediateSyncWorker() {
val oneTimeWork = OneTimeWorkRequestBuilder().build()
WorkManager.getInstance(this)
.enqueueUniqueWork(
"immediate_anime_sync_and_reminder_work",
ExistingWorkPolicy.REPLACE,
oneTimeWork
)
}
}

@HiltWorker
class ReminderWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {

override suspend fun doWork(): Result {
val title = inputData.getString("title") ?: return Result.failure()
val episodeNumber = inputData.getInt("episodeNumber", 0)
val episodeDateMillis = inputData.getLong("episodeDateMillis", System.currentTimeMillis())

showNotification(applicationContext, title, episodeNumber, episodeDateMillis)
return Result.success()
}

companion object {
fun showNotification(context: Context, title: String, episodeNumber: Int, episodeDateMillis: Long) {
val formatter = DateTimeFormatter.ofPattern("HH:mm")
.withLocale(Locale.getDefault())
.withZone(ZoneId.systemDefault())
val formattedTime = formatter.format(Instant.ofEpochMilli(episodeDateMillis))

val message = "¡El episodio $episodeNumber de $title se estrenará a las $formattedTime!"

val channelId = "notaku_channel"
val notificationManager = context.getSystemService(NotificationManager::class.java)

if (notificationManager.getNotificationChannel(channelId) == null) {
val channel = NotificationChannel(
channelId,
"Notaku Notifications",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}

val builder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.notaku_logo_black)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)

notificationManager.notify(System.currentTimeMillis().toInt(), builder.build())
}
}
}

@HiltWorker
class AnimeSyncAndReminderWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val syncUserTrackedAnimesUseCase: SyncUserTrackedAnimesUseCase,
private val getTrackedAnimesUseCase: GetTrackedAnimesUseCase,
private val getUserPreferencesUseCase: GetUserPreferencesUseCase
) : CoroutineWorker(context, workerParams) {

override suspend fun doWork(): Result {
return try {
syncUserTrackedAnimesUseCase.execute()
scheduleEpisodeReminders(getTrackedAnimesUseCase.execute())

Result.success()
} catch (e: Exception) {
e.printStackTrace()
Result.retry()
}
}

private suspend fun scheduleEpisodeReminders(animes: List) {
val context = applicationContext
val workManager = WorkManager.getInstance(context)
val reminderMinutes = getUserReminderMinutes()
val now = System.currentTimeMillis()

for (anime in animes) {
val episodeDateMillis = parseDateToMillis(anime.episodeDate) ?: continue
val delayMillis = episodeDateMillis - now - reminderMinutes * 60_000
val workName = "reminder_${anime.title}_${anime.episodeNumber}"
val data = workDataOf(
"title" to anime.title,
"episodeNumber" to anime.episodeNumber,
"episodeDateMillis" to episodeDateMillis
)

val existing = workManager.getWorkInfosForUniqueWork(workName).get()
val alreadyScheduled = existing.any {
it.state == WorkInfo.State.ENQUEUED &&
it.outputData.getLong("episodeDateMillis", 0) == episodeDateMillis
}

if (alreadyScheduled) {
Log.d(TagHandler.NOTAKU_INFO, "🔁 Already scheduled ${anime.title} - skipping")
continue
}

if (delayMillis > 0) {
val workRequest = OneTimeWorkRequestBuilder()
.setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
.setInputData(data)
.build()

workManager.enqueueUniqueWork(workName, ExistingWorkPolicy.REPLACE, workRequest)

}
}
}

private fun parseDateToMillis(dateString: String): Long? {
return try {
val odt = OffsetDateTime.parse(dateString)
odt.toInstant().toEpochMilli()
} catch (e: Exception) {
Log.e(TagHandler.NOTAKU_ERROR, "Error parsing date: $dateString", e)
null
}
}

private suspend fun getUserReminderMinutes(): Int {
return getUserPreferencesUseCase()
.firstOrNull()
?.notificationTime ?: 30
}
}








Подробнее здесь: https://stackoverflow.com/questions/798 ... ces-workma
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»