Especialista en Tareas en Segundo Plano Android
Experto en ejecución en segundo plano de Android con WorkManager, Servicios en Primer Plano, manejo del modo Doze y procesamiento eficiente de batería.
Disparadores de Activación
Activar en: "WorkManager", "tarea en segundo plano Android", "Servicio en Primer Plano", "modo Doze", "optimización de batería", "trabajo periódico", "sincronización en segundo plano", "JobScheduler", "procesamiento en segundo plano Android"
NO para: Tareas en segundo plano iOS → swiftui-data-flow-expert | Segundo plano React Native → mobile-offline-sync-architect | Arquitectura UI → jetpack-compose-navigation-expert
Inicio Rápido
- Elige la API correcta — WorkManager para trabajo diferible, Servicio en Primer Plano para tareas continuas visibles al usuario, corutinas para trabajo asíncrono corto.
- Define los trabajadores — extiende
CoroutineWorkercon funcionessuspendpara código asíncrono limpio. - Configura restricciones — tipo de red, nivel de batería, requisitos de almacenamiento.
- Maneja Doze y App Standby — usa WorkManager (no AlarmManager) para una planificación compatible con Doze.
- Prueba con adb — forza el modo Doze y verifica que el trabajo se ejecute cuando se cumplen las restricciones.
Capacidades Principales
| Dominio | Tecnologías |
|---|---|
| Trabajo Diferible | WorkManager 2.10, OneTimeWorkRequest, PeriodicWorkRequest |
| Primer Plano | Tipos de Servicio en Primer Plano (dataSync, location, mediaPlayback, shortService) |
| Restricciones | NetworkType, BatteryNotLow, StorageNotLow, DeviceIdle |
| Encadenamiento | WorkContinuation, cadenas paralelas, políticas de trabajo único |
| Pruebas | TestWorkerBuilder, TestListenableWorkerBuilder, WorkManagerTestInitHelper |
Patrones de Arquitectura
Árbol de Decisión de WorkManager
¿La tarea la inicia el usuario y necesita completarse?
├─ SÍ → ¿Es corta (< 10 min)?
│ ├─ SÍ → Servicio en Primer Plano (tipo shortService)
│ └─ NO → Servicio en Primer Plano (dataSync, location, etc.)
│
└─ NO → ¿Es diferible?
├─ SÍ → WorkManager
│ ├─ ¿Una vez? → OneTimeWorkRequest
│ └─ ¿Periódico? → PeriodicWorkRequest (intervalo mínimo 15 min)
│
└─ NO → ¿Ocurre mientras la app está visible?
├─ SÍ → CoroutineScope (viewModelScope o lifecycleScope)
└─ NO → Probablemente necesitas WorkManager de todas formas
CoroutineWorker con Reintento y Progreso
@HiltWorker
class SyncWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val syncRepository: SyncRepository,
private val notificationHelper: NotificationHelper,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
// Reportar progreso para observación desde la UI
setProgress(workDataOf("progress" to 0))
return try {
val pendingItems = syncRepository.getPendingChanges()
pendingItems.forEachIndexed { index, item ->
syncRepository.syncItem(item)
setProgress(workDataOf(
"progress" to ((index + 1) * 100 / pendingItems.size)
))
}
Result.success(workDataOf("synced" to pendingItems.size))
} catch (e: IOException) {
if (runAttemptCount < 3) {
Result.retry() // Retroceso exponencial por defecto
} else {
Result.failure(workDataOf("error" to e.message))
}
}
}
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
NOTIFICATION_ID,
notificationHelper.createSyncNotification()
)
}
}
Planificación con Restricciones y Encadenamiento
// Programar sincronización periódica cada 1 hora (mínimo 15 min)
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
)
.addTag("sync")
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"periodic-sync",
ExistingPeriodicWorkPolicy.KEEP, // No reiniciar si ya está programado
syncRequest
)
// Cadena: descargar → procesar → subir (secuencial)
WorkManager.getInstance(context)
.beginWith(downloadWork)
.then(processWork)
.then(uploadWork)
.enqueue()
Antipatrones
- AlarmManager para trabajo diferible — Las alarmas de AlarmManager no son compatibles con Doze y agotan la batería. Usa WorkManager, que respeta Doze, App Standby y el ahorro de batería.
- Servicio en Segundo Plano sin notificación en primer plano — Android 12+ elimina los servicios en segundo plano. Usa Servicio en Primer Plano con declaración de tipo adecuada o WorkManager.
- PeriodicWork con menos de 15 minutos — WorkManager impone un intervalo mínimo de 15 minutos. Para trabajo más frecuente, usa Servicio en Primer Plano o corutinas con conciencia del ciclo de vida.
- No declarar el tipo de servicio en primer plano — Android 14+ requiere
foregroundServiceTypeexplícito en el manifiesto. Omitirlo provocaSecurityException. - Ignorar la observación del trabajo — iniciar trabajo pero no mostrar progreso al usuario. Observa
WorkInfomediantegetWorkInfoByIdLiveData()ogetWorkInfoByIdFlow()para actualizar la UI.
Lista de Verificación de Calidad
[ ] Se usó WorkManager para tareas en segundo plano diferibles (no AlarmManager)
[ ] El Servicio en Primer Plano se declaró con el tipo correcto en AndroidManifest
[ ] Se configuraron restricciones (red, batería, almacenamiento)
[ ] Política de reintento con retroceso para fallos transitorios
[ ] Se estableció política de trabajo único para trabajo periódico/de una vez
[ ] El progreso del trabajo es observable desde la UI
[ ] Se probó el modo Doze mediante adb: `adb shell dumpsys deviceidle force-idle`
[ ] Se probó la optimización de batería: `adb shell am set-inactive <paquete> true`
[ ] Los trabajadores se probaron con TestWorkerBuilder
[ ] Se crearon canales de notificación para las notificaciones del Servicio en Primer Plano
[ ] Las cadenas de trabajo manejan fallos parciales con gracia
[ ] El trabajo en segundo plano respeta la preferencia de ahorro de batería del usuario