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

Android治疗监测APP中WorkManager运行一段时间后停止工作问题排查

问题背景与代码

我正在开发一款基于Jetpack Compose + Kotlin的Android日常治疗监测APP,用户设置的每日任务(如服药、测血压等)存储在Room数据库中。我希望APP每30分钟检查一次未来30分钟内是否有即将到来的任务,若有则推送通知。我选择WorkManager实现该需求,因为希望APP在未激活状态下也能发送通知。

相关代码

MainActivity 代码

//MainActivity
private lateinit var workManager: WorkManager
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        workManager = WorkManager.getInstance(applicationContext)
        val workRequest = PeriodicWorkRequestBuilder<IncomingTherapyWorker>(30, TimeUnit.MINUTES)
            .setInitialDelay(1, TimeUnit.MINUTES)
            .build()
        workManager.enqueueUniquePeriodicWork(IncomingTherapyWorker.WORKER_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, workRequest)

        //Notification channel creation
        val therapyNotificationService = TherapyNotificationService(this)
        therapyNotificationService.createNotificationChannel()
        //
        setContent {...

IncomingTherapyWorker 代码

@HiltWorker
class IncomingTherapyWorker @AssistedInject constructor(
    @Assisted context: Context,
    @Assisted parameters: WorkerParameters,
    private val repository: TherapyRepository
) : CoroutineWorker(context, parameters) {

    val terapyNotificationService = TherapyNotificationService(context = context)

    override suspend fun doWork(): Result {
        return try {

            //let's log on a file
            val dataNow = LocalDateTime.now()
            val folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
            val file = File(folder,"RegistroWorker.txt")
            val outputStream = withContext(Dispatchers.IO) {
                FileOutputStream(file, true)
            }

            withContext(Dispatchers.IO) {
                outputStream.write(
                    String.format(
                        "%4d_%02d_%02d_%02d_%02d_%02d \n",
                        dataNow.year,
                        dataNow.monthValue,
                        dataNow.dayOfMonth,
                        dataNow.hour,
                        dataNow.minute,
                        dataNow.second
                    ).toByteArray()
                )
                outputStream.close()
            }
            //

            var messaggio: String = ""
            val listaTerapie = repository.getTherapylist()
            val iterator = listaTerapie.listIterator()
            iterator.forEach { terapia ->
                val incomingOutput = isTherapyIncoming(terapia)
                if(incomingOutput.esito) {
                    messaggio = messaggio + terapia.etichetta + " " + incomingOutput.messaggio + "\n"
                }
            }

            if(!messaggio.isEmpty())
                terapyNotificationService.sendTherapyNotification(messaggio)

            return Result.success()
        } catch (throwable: Throwable) {
            throwable.message?.let { Log.d(WORKER_TAG, it) }
            throwable.cause?.let { Log.d(WORKER_TAG, it.cause.toString()) }
            Result.failure()
        }
    }

    companion object {
        const val WORKER_TAG = "TherapySeeker"
    }
}

疑问

当前代码能正常运行,但一段时间后会停止工作,尤其是在设备关机或APP后台运行时。我猜测这可能与Doze模式和电池优化有关。请问我的代码是否存在问题?是否能实现全场景下持续运行的Worker?


解答

代码本身的问题

  • 文件写入资源泄漏风险:手动操作FileOutputStream时,若写入过程抛出异常,close()可能无法执行,导致资源泄漏。建议改用Kotlin的use函数自动管理资源:
    withContext(Dispatchers.IO) {
        FileOutputStream(file, true).use { outputStream ->
            outputStream.write(
                String.format(
                    "%4d_%02d_%02d_%02d_%02d_%02d \n",
                    dataNow.year,
                    dataNow.monthValue,
                    dataNow.dayOfMonth,
                    dataNow.hour,
                    dataNow.minute,
                    dataNow.second
                ).toByteArray()
            )
        }
    }
    
  • 周期性任务执行精度不足:PeriodicWorkRequest的30分钟间隔是弹性的,Android系统会根据Doze模式、电池策略调整执行时机,设备休眠时会延迟任务,这是你遇到的核心问题。

关于全场景持续运行的Worker

Android从6.0开始的Doze模式、App Standby,加上各厂商的定制化电池管控,没有任何API能保证Worker在所有场景下100%持续运行。设备关机后所有后台任务终止,开机后WorkManager会尝试恢复,但完全依赖系统调度。

优化建议

  • 适配电池优化:
    • 引导用户将APP加入电池优化白名单,通过ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS申请权限,需合理说明用途避免Google Play审核问题。
    • Android 12及以上,申请ExactAlarmPermission精确闹钟权限,结合AlarmManager的精确闹钟触发任务,比WorkManager的周期性任务更准时,但仍需遵守系统限制。
  • 调整WorkManager配置:
    • 用ExistingPeriodicWorkPolicy.KEEP替代CANCEL_AND_REENQUEUE,避免每次启动APP都重建任务,减轻系统调度负担。
    • 若任务非紧急,可添加setConstraints指定REQUIRES_BATTERY_NOT_LOW等约束,但医疗类提醒不建议这么做。
  • 医疗类场景特殊处理:
    • 使用高优先级通知渠道,确保用户及时收到提醒;在通知中添加“稍后提醒”按钮,通过OneTimeWorkRequest快速触发下一次检查。
    • 注册BootCompletedReceiver,设备开机时重新初始化WorkManager任务,避免任务丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:21:05