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的周期性任务更准时,但仍需遵守系统限制。
- 引导用户将APP加入电池优化白名单,通过
- 调整WorkManager配置:
- 用
ExistingPeriodicWorkPolicy.KEEP替代CANCEL_AND_REENQUEUE,避免每次启动APP都重建任务,减轻系统调度负担。 - 若任务非紧急,可添加
setConstraints指定REQUIRES_BATTERY_NOT_LOW等约束,但医疗类提醒不建议这么做。
- 用
- 医疗类场景特殊处理:
- 使用高优先级通知渠道,确保用户及时收到提醒;在通知中添加“稍后提醒”按钮,通过
OneTimeWorkRequest快速触发下一次检查。 - 注册
BootCompletedReceiver,设备开机时重新初始化WorkManager任务,避免任务丢失。
- 使用高优先级通知渠道,确保用户及时收到提醒;在通知中添加“稍后提醒”按钮,通过
内容的提问来源于stack exchange,提问作者Marco Chiesi
相关产品推荐
相关产品推荐

