30 mar 2025·8 min de lectura

Patrones de sincronización en segundo plano con Kotlin WorkManager para apps de campo

Patrones de sincronización en segundo plano con Kotlin WorkManager para apps de campo: elige el tipo de trabajo correcto, configura restricciones, usa retroceso exponencial y muestra progreso visible al usuario.

Patrones de sincronización en segundo plano con Kotlin WorkManager para apps de campo

Lo que significa sincronización en segundo plano fiable para apps de campo y de operaciones

En las apps de campo y operaciones, la sincronización no es un "extra agradable". Es la forma en que el trabajo sale del dispositivo y se vuelve real para el equipo. Cuando la sincronización falla, los usuarios se dan cuenta rápido: un trabajo completado sigue apareciendo como “pendiente”, desaparecen fotos o el mismo informe se sube dos veces y crea duplicados.

Estas apps son más exigentes que las apps de consumo típicas porque los teléfonos funcionan en las peores condiciones. La red alterna entre LTE, Wi‑Fi débil y sin señal. El ahorro de batería bloquea trabajo en segundo plano. La app se cierra, el SO se actualiza y los dispositivos reinician a mitad de ruta. Una configuración fiable de WorkManager tiene que sobrevivir todo eso sin drama.

Fiable suele significar cuatro cosas:

  • Eventualmente consistente: los datos pueden llegar tarde, pero llegan sin babysitting manual.
  • Recuperable: si la app muere a mitad de subida, la siguiente ejecución continúa de forma segura.
  • Observable: usuarios y soporte pueden saber qué está pasando y qué está atascado.
  • No destructivo: los reintentos no crean duplicados ni corrompen el estado.

“Ejecutar ahora” encaja en acciones pequeñas iniciadas por el usuario que deberían terminar pronto (por ejemplo, enviar una sola actualización de estado antes de cerrar un trabajo). “Esperar” encaja en tareas más pesadas como subir fotos, actualizaciones por lotes o cualquier cosa que pueda agotar la batería o fallar con malas redes.

Ejemplo: un inspector envía un formulario con 12 fotos en un sótano sin señal. Una sincronización fiable guarda todo localmente, lo marca como en cola y lo sube después cuando el dispositivo tiene una conexión real, sin que el inspector tenga que rehacer el trabajo.

Elige los bloques correctos de WorkManager

Empieza eligiendo la unidad más pequeña y clara de trabajo. Esa decisión afecta la fiabilidad más que cualquier lógica de reintento ingeniosa que añadas después.

One-time vs periodic work

Usa OneTimeWorkRequest para trabajo que debe ocurrir porque algo cambió: se guardó un nuevo formulario, terminó la compresión de una foto o el usuario tocó Sincronizar. Encróvalo enseguida (con restricciones) y deja que WorkManager lo ejecute cuando el dispositivo esté listo.

Usa PeriodicWorkRequest para mantenimiento constante, como un “comprobar actualizaciones” o limpieza nocturna. El trabajo periódico no es exacto. Tiene un intervalo mínimo y puede desviarse según reglas del sistema y la batería, así que no debería ser la única vía para subidas importantes.

Un patrón práctico es trabajo one-time para “debe sincronizar pronto” y trabajo periódico como red de seguridad.

Elegir Worker, CoroutineWorker o RxWorker

Si programas en Kotlin y usas funciones suspend, prefiere CoroutineWorker. Mantiene el código corto y hace que la cancelación se comporte como esperas.

Worker encaja con código bloqueante simple, pero debes tener cuidado de no bloquear demasiado tiempo.

RxWorker tiene sentido sólo si tu app ya usa RxJava intensamente. Si no, añade complejidad innecesaria.

Encadenar pasos o usar un worker con fases?

Encadenar es genial cuando los pasos pueden fallar o tener éxito de forma independiente y quieres reintentos separados y logs más claros. Un worker con fases puede ser mejor cuando los pasos comparten datos y deben tratarse como una sola transacción.

Una regla simple:

  • Encadena cuando los pasos tienen diferentes restricciones (subida solo por Wi‑Fi, y luego una llamada API ligera).
  • Usa un worker cuando necesitas una sincronización “todo o nada”.

WorkManager garantiza que el trabajo se persiste, puede sobrevivir a la muerte del proceso y reinicios, y respeta restricciones. No garantiza ejecución exacta, inmediata o que se ejecute después de que el usuario fuerce la detención de la app. Si construyes una app de campo Android (incluida una generada en Kotlin desde AppMaster), diseña la sincronización para que los retrasos sean seguros y esperables.

Haz la sincronización segura: idempotente, incremental y reanudable

Una app de campo volverá a ejecutar trabajo. Los teléfonos pierden señal, el SO mata procesos y los usuarios tocan sincronizar dos veces porque no vieron nada. Si tu sincronización en segundo plano no es segura para repetirse, tendrás registros duplicados, actualizaciones perdidas o reintentos infinitos.

Empieza por hacer cada llamada al servidor segura para ejecutarse dos veces. El enfoque más simple es una clave de idempotencia por ítem (por ejemplo, un UUID almacenado con el registro local) que el servidor trate como “misma petición, mismo resultado”. Si no puedes cambiar el servidor, usa una clave natural estable y un endpoint upsert, o incluye un número de versión para que el servidor rechace actualizaciones obsoletas.

Rastrea el estado local explícitamente para que el worker pueda reanudar tras un fallo sin adivinar. Una máquina de estados sencilla suele bastar:

  • queued
  • uploading
  • uploaded
  • needs-review
  • failed-temporary

Mantén la sincronización incremental. En lugar de “sincronizar todo”, guarda un cursor como lastSuccessfulTimestamp o un token emitido por el servidor. Lee una página pequeña de cambios, aplícalos y avanza el cursor sólo cuando el lote se haya comprometido localmente. Lotes pequeños (20–100 ítems) reducen timeouts, hacen visible el progreso y limitan cuánto trabajo repites tras una interrupción.

Haz las subidas reanudables también. Para fotos o cargas grandes, persiste el URI del archivo y metadatos de la subida, y marca como subido sólo tras la confirmación del servidor. Si el worker se reinicia, continúa desde el último estado conocido en vez de empezar de nuevo.

Ejemplo: un técnico rellena 12 formularios y adjunta 8 fotos sin cobertura. Cuando el dispositivo se reconecta, el worker sube en lotes; cada formulario tiene una clave de idempotencia y el cursor avanza sólo después de que cada lote tenga éxito. Si la app se cierra a mitad, volver a ejecutar el worker completa los ítems restantes sin duplicar nada.

Restricciones que coincidan con condiciones reales del dispositivo

Las restricciones son las barandillas que evitan que la sincronización agote baterías, queme planes de datos o falle en el peor momento. Quieres restricciones que reflejen cómo se comportan los dispositivos en el campo, no en tu mesa.

Empieza con un conjunto pequeño que proteja a los usuarios pero permita que el trabajo se ejecute la mayoría de los días. Una base práctica es: requerir conexión de red, evitar ejecución con batería baja y evitar ejecución con almacenamiento críticamente bajo. Añade “cargando” sólo si el trabajo es pesado y no sensible al tiempo, porque muchos dispositivos de campo rara vez están conectados durante el turno.

Exigir demasiado es una razón común por la que la sincronización “nunca corre”. Si pides Wi‑Fi no medido, cargando y batería no baja, básicamente pides un momento perfecto que puede no llegar. Si el negocio necesita datos hoy, es mejor ejecutar trabajo más pequeño con más frecuencia que esperar condiciones ideales.

Los portales cautivos son otro problema real: el teléfono dice que está conectado, pero el usuario debe tocar “Aceptar” en la página de un hotel o Wi‑Fi público. WorkManager no puede detectar eso de forma fiable. Trátalo como un fallo normal: intenta la sincronización, haz timeout rápido y reintenta después. También muestra un mensaje sencillo en la app como “Conectado a Wi‑Fi pero sin acceso a Internet” cuando puedas detectarlo durante la petición.

Usa diferentes restricciones para subidas pequeñas vs grandes para que la app siga respondiendo:

  • Cargas pequeñas (pings de estado, metadatos de formularios): cualquier red, batería no baja.
  • Cargas grandes (fotos, vídeos, paquetes de mapas): red no medida si es posible, y considera requerir carga.

Ejemplo: un técnico guarda un formulario con 2 fotos. Envía los campos del formulario en cualquier conexión, pero encola las fotos para esperar Wi‑Fi o un mejor momento. La oficina ve el trabajo rápido y el dispositivo no consume datos móviles subiendo imágenes en segundo plano.

Reintentos con retroceso exponencial que no molesten a los usuarios

Ejecuta un pequeño piloto de campo
Lanza primero una pieza fiable, por ejemplo un formulario de inspección con fotos y reintentos seguros.
Iniciar piloto

Los reintentos son donde las apps de campo se sienten tranquilas o rotas. Elige una política de backoff que coincida con el tipo de fallo que esperas.

El retroceso exponencial suele ser el valor por defecto más seguro para redes. Aumenta rápidamente el tiempo de espera para no saturar el servidor ni agotar la batería cuando la cobertura es mala. El backoff lineal puede servir para problemas cortos y temporales (por ejemplo, una VPN inestable), pero tiende a reintentar demasiado a menudo en áreas de señal débil.

Toma decisiones de reintento en función del tipo de error, no sólo “algo falló”. Un conjunto de reglas simple ayuda:

  • Timeout de red, 5xx, DNS, sin conectividad: Result.retry()
  • Auth expirado (401): refrescar token una vez, luego fallar y pedir al usuario iniciar sesión
  • Validación o 4xx (bad request): Result.failure() con un error claro para soporte
  • Conflicto (409) para ítems ya enviados: tratar como éxito si tu sincronización es idempotente

Limita el daño para que un error permanente no entre en bucle infinito. Fija un número máximo de intentos y, tras eso, para y muestra un mensaje discreto y accionable (no notificaciones repetidas).

También puedes cambiar el comportamiento a medida que aumentan los intentos. Por ejemplo, tras 2 fallos, envía lotes más pequeños o evita subidas grandes hasta el siguiente pull exitoso.

val request = OneTimeWorkRequestBuilder<SyncWorker>()
  .setBackoffCriteria(
    BackoffPolicy.EXPONENTIAL,
    30, TimeUnit.SECONDS
  )
  .build()

// in doWork()
if (runAttemptCount >= 5) return Result.failure()
return Result.retry()

Esto mantiene los reintentos educados: menos despertadas, menos interrupciones al usuario y recuperación más rápida cuando la conexión vuelve.

Progreso visible al usuario: notificaciones, trabajo en primer plano y estado

Convierte modelos de datos en apps
Modela tus datos en minutos y conviértelos en APIs y pantallas que realmente puedas lanzar.
Comenzar a crear

Las apps de campo a menudo sincronizan cuando el usuario menos lo espera: en un sótano, con red lenta o con batería casi muerta. Si la sincronización afecta a lo que el usuario está esperando (subidas, envío de informes, lotes de fotos), hazla visible y fácil de entender. El trabajo silencioso en segundo plano está bien para actualizaciones pequeñas y rápidas. Cualquier operación más larga debe sentirse honesta.

Cuándo se requiere trabajo en primer plano

Usa ejecución en primer plano cuando un trabajo es de larga duración, sensible al tiempo o claramente ligado a una acción del usuario. En Android moderno, grandes subidas pueden ser detenidas o retrasadas a menos que ejecutes en primer plano. En WorkManager, eso significa devolver un ForegroundInfo para que el sistema muestre una notificación en curso.

Una buena notificación responde tres preguntas: qué se está sincronizando, cuánto falta y cómo detenerlo. Añade una acción clara de cancelar para que el usuario pueda desistir si está en datos medidos o necesita el teléfono ahora.

Progreso en el que la gente confía

El progreso debe mapearse a unidades reales, no a porcentajes vagos. Actualiza el progreso con setProgress y léelo desde WorkInfo en tu UI (o una pantalla de estado).

Si subes 12 fotos y 3 formularios, informa “5 de 15 ítems subidos”, muestra lo que queda y guarda el último mensaje de error para soporte.

Mantén el progreso significativo:

  • Ítems completados e ítems restantes
  • Paso actual ("Subiendo fotos", "Enviando formularios", "Finalizando")
  • Última sincronización exitosa
  • Último error (breve, amigable)
  • Una opción visible de cancelar/detener

Si tu equipo genera herramientas internas rápidamente con AppMaster, aplica la misma regla: los usuarios confían en la sincronización cuando pueden verla y cuando corresponde con lo que intentan hacer.

Trabajo único, tags y evitar trabajos duplicados de sincronización

Los trabajos de sincronización duplicados son una de las formas más fáciles de agotar batería, consumir datos móviles y crear conflictos en el servidor. WorkManager te da dos herramientas sencillas para evitar eso: nombres de trabajo únicos y tags.

Un buen valor por defecto es tratar “sync” como una sola vía. En lugar de encolar un trabajo nuevo cada vez que la app despierta, encola el mismo nombre de trabajo único. Así no tendrás una tormenta de sincronizaciones cuando el usuario abra la app, cambie la red y un trabajo periódico se dispare al mismo tiempo.

val request = OneTimeWorkRequestBuilder<SyncWorker>()
  .addTag("sync")
  .build()

WorkManager.getInstance(context)
  .enqueueUniqueWork("sync", ExistingWorkPolicy.KEEP, request)

Elegir la política es la decisión de comportamiento principal:

  • KEEP: si ya hay una sincronización en ejecución (o en cola), ignora la nueva petición. Usa esto para la mayoría de botones “Sincronizar ahora” y disparadores automáticos.
  • REPLACE: cancela la actual y empieza de nuevo. Úsalo cuando las entradas realmente cambiaron, como al cambiar de cuenta o seleccionar otro proyecto.

Los tags son tu asa para control y visibilidad. Con un tag estable como sync, puedes cancelar, consultar estado o filtrar logs sin rastrear IDs específicos. Esto es útil para una acción manual “sincronizar ahora”: puedes comprobar si ya hay trabajo en marcha y mostrar un mensaje claro en lugar de lanzar otro worker.

La sincronización periódica y la on‑demand no deberían pelear entre sí. Mantenlas separadas, pero coordinadas:

  • Usa enqueueUniquePeriodicWork("sync_periodic", KEEP, ...) para el trabajo programado.
  • Usa enqueueUniqueWork("sync", KEEP, ...) para on‑demand.
  • En tu worker, sal pronto si no hay nada que subir o descargar, para que la ejecución periódica siga siendo barata.
  • Opcionalmente, que el worker periódico encole el mismo trabajo one‑time único, para que todo el trabajo real ocurra en un solo lugar.

Estos patrones mantienen la sincronización predecible: una sincronización a la vez, fácil de cancelar y fácil de observar.

Paso a paso: un pipeline de sincronización práctico

Añade integraciones de forma práctica
Conecta autentificación, pagos, mensajería y AI sin tener que unir docenas de SDKs.
Construir integraciones

Un pipeline fiable es más fácil de construir si lo tratas como una pequeña máquina de estados: los ítems viven localmente primero y WorkManager sólo los mueve adelante cuando las condiciones son adecuadas.

Un pipeline simple que puedes lanzar

  1. Empieza con tablas locales de “cola”. Guarda los metadatos mínimos para reanudar: id del ítem, tipo (formulario, foto, nota), estado (pending, uploading, done), contador de intentos, último error y un cursor o revisión del servidor para descargas.

  2. Para un “Sincronizar ahora” tocado por el usuario, encola un OneTimeWorkRequest con restricciones que coincidan con el mundo real. Opciones comunes: red conectada y batería no baja. Si las subidas son pesadas, también requerir carga.

  3. Implementa un CoroutineWorker con fases claras: upload, download, reconcile. Mantén cada fase incremental. Sube sólo ítems marcados como pending, descarga sólo cambios desde tu último cursor y luego reconcilia conflictos con reglas sencillas (por ejemplo: el servidor gana en campos de asignación, el cliente gana en notas borrador locales).

  4. Añade reintentos con backoff, pero sé selectivo sobre qué reintentas. Timeouts y 500s deben reintentar. Un 401 (sesión cerrada) debe fallar rápido y decir a la UI qué pasó.

  5. Observa WorkInfo para conducir la UI y notificaciones. Usa actualizaciones de progreso para fases como “Subiendo 3 de 10” y muestra un mensaje breve de fallo que apunte a la siguiente acción (reintentar, iniciar sesión, conectarse a Wi‑Fi).

val constraints = Constraints.Builder()
  .setRequiredNetworkType(NetworkType.CONNECTED)
  .setRequiresBatteryNotLow(true)
  .build()

val request = OneTimeWorkRequestBuilder<SyncWorker>()
  .setConstraints(constraints)
  .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
  .build()

Cuando mantienes la cola local y las fases del worker explícitas, obtienes un comportamiento predecible: el trabajo puede pausar, reanudar y explicarse al usuario sin adivinar qué pasó.

Errores comunes y trampas (y cómo evitarlos)

La sincronización fiable suele fallar por unas pocas decisiones pequeñas que parecen inocuas en pruebas, pero se desmoronan en dispositivos reales. El objetivo no es ejecutar lo más seguido posible. Es ejecutarlo en el momento correcto, hacer el trabajo adecuado y detenerse limpiamente cuando no puede.

Trampas a vigilar

  • Hacer subidas grandes sin restricciones. Si subes fotos o cargas grandes en cualquier red y con cualquier nivel de batería, los usuarios lo notarán. Añade restricciones por tipo de red y batería baja, y divide trabajo grande en trozos más pequeños.
  • Reintentar todo fallo para siempre. Un 401, token expirado o permiso faltante no es temporal. Márcalo como fallo duro, muestra una acción clara (re‑login) y reintenta sólo problemas verdaderamente transitorios como timeouts.
  • Crear duplicados por accidente. Si un worker puede ejecutarse dos veces, el servidor verá duplicados a menos que las peticiones sean idempotentes. Usa un ID generado en cliente por ítem y que el servidor trate repeticiones como actualizaciones, no como nuevas filas.
  • Usar trabajo periódico para necesidades casi en tiempo real. El trabajo periódico es mejor para mantenimiento, no para “sincronizar ahora”. Para sincronización iniciada por el usuario, encola trabajo one‑time único y permite que el usuario lo dispare cuando haga falta.
  • Reportar “100%” demasiado pronto. La finalización de la subida no es lo mismo que la aceptación y reconciliación por parte del servidor. Sigue el progreso por etapas (queued, uploading, server confirmed) y sólo muestra completado después de la confirmación.

Un ejemplo concreto: un técnico envía un formulario con tres fotos en un ascensor con señal débil. Si empiezas inmediatamente sin restricciones, las subidas se atascan, los reintentos se disparan y el formulario puede crearse dos veces cuando la app se reinicia. Si restrinjes a una red usable, subes en pasos y claves cada formulario con un ID estable, el mismo escenario acaba con un único registro en el servidor y un mensaje de progreso veraz.

Lista rápida antes de lanzar

Construye tu app de campo más rápido
Crea una app lista para campo con un backend real y clientes móviles nativos, sin codificarlo todo a mano.
Probar AppMaster

Antes de publicar, prueba la sincronización como la romperán los usuarios de campo: señal intermitente, baterías muertas y muchos toques. Lo que parece bien en un teléfono de desarrollo puede fallar en el campo si la programación, los reintentos o el reporte de estado son inadecuados.

Haz estas comprobaciones al menos en un dispositivo lento y uno más nuevo. Mantén logs, pero también mira lo que el usuario ve en la UI.

  • Sin red y luego recuperación: Inicia una sincronización sin conectividad, luego enciéndela. Confirma que el trabajo queda en cola (no falla rápido) y se reanuda después sin duplicar subidas.
  • Reinicio del dispositivo: Comienza una sincronización, reinicia a mitad y abre la app. Verifica que el trabajo continúa o se reprograma correctamente y que la app muestra el estado actual correcto (no atascado en "sincronizando").
  • Batería baja y poco almacenamiento: Activa ahorro de batería, baja por debajo del umbral de batería baja si es posible y llena el almacenamiento casi por completo. Confirma que el trabajo espera cuando debe y luego continúa cuando las condiciones mejoran, sin agotar batería en un bucle de reintentos.
  • Disparadores repetidos: Toca varias veces el botón "Sincronizar" o dispara la sincronización desde varias pantallas. Deberías terminar con una sola ejecución lógica de sincronización, no con varios workers paralelos compitiendo por los mismos registros.
  • Fallos de servidor que puedes explicar: Simula 500s, timeouts y errores de autenticación. Comprueba que los reintentos usan backoff y se detienen tras un límite, y que el usuario ve un mensaje claro como "No se puede contactar con el servidor, se reintentará" en lugar de un fallo genérico.

Si alguna prueba deja la app en un estado poco claro, trátalo como un bug. Los usuarios perdonan sincronizaciones lentas, pero no perder datos ni no saber qué pasó.

Escenario de ejemplo: formularios offline y subida de fotos en una app de campo

Una plataforma para todo el stack
Construye backend, app web y apps móviles nativas juntos para que modelos y auth permanezcan alineados.
Crear app

Un técnico llega a un sitio con cobertura débil. Rellena un formulario de servicio, captura 12 fotos y toca Enviar antes de irse. La app guarda todo localmente (por ejemplo, en una base de datos local): un registro para el formulario y un registro por foto con un estado claro como PENDING, UPLOADING, DONE o FAILED.

Cuando toca Enviar, la app encola un trabajo de sincronización único para no crear duplicados si pulsa dos veces. Una configuración común es una cadena de WorkManager que sube fotos primero (más grandes y lentas) y luego envía la carga del formulario tras confirmar los adjuntos.

La sincronización corre sólo cuando las condiciones coinciden con la vida real. Por ejemplo, espera red conectada, batería no baja y suficiente almacenamiento. Si el técnico sigue en el sótano sin señal, nada consumirá batería en bucles infinitos.

El progreso es obvio y amigable. La subida se ejecuta en primer plano y muestra una notificación como “Subiendo 3 de 12”, con una acción clara Cancelar. Si cancelan, la app detiene el trabajo y mantiene los ítems restantes en PENDING para que puedan reintentar después sin perder datos.

Los reintentos se comportan de forma educada tras un hotspot inestable: el primer fallo reintenta pronto, pero cada fallo espera más (retroceso exponencial). Al principio se siente sensible, luego se desacelera para evitar agotar batería y saturar la red.

Para el equipo de operaciones, la recompensa es práctica: menos envíos duplicados porque los ítems son idempotentes y están en cola única, estados de fallo claros (qué foto falló, por qué y cuándo se reintentará) y mayor confianza de que “enviado” significa “almacenado de forma segura y se sincronizará”.

Próximos pasos: prioriza fiabilidad y luego amplía el alcance de la sincronización

Antes de añadir más funciones de sincronización, define claramente qué significa “hecho”. Para la mayoría de apps de campo no es “petición enviada”. Es “servidor aceptó y confirmó”, más un estado UI que coincida con la realidad. Un formulario que muestre “Sincronizado” debe mantener ese estado tras reinicios de la app, y un formulario que falló debe mostrar qué hacer a continuación.

Haz que la app sea fácil de confiar añadiendo un pequeño conjunto de señales que la gente pueda ver (y que soporte pueda preguntar). Mantenlas simples y consistentes en las pantallas:

  • Última sincronización exitosa
  • Último error de sincronización (mensaje corto, no stack trace)
  • Ítems pendientes (por ejemplo: 3 formularios, 12 fotos)
  • Estado actual de sincronización (Idle, Syncing, Needs attention)

Trata la observabilidad como parte de la funcionalidad. Ahorra horas en el campo cuando alguien está con mala conexión y no sabe si la app está funcionando.

Si construyes también el backend y las herramientas admin, generarlas juntas ayuda a mantener estable el contrato de sincronización. AppMaster (appmaster.io) puede generar un backend listo para producción, un panel web admin y apps móviles nativas, lo que ayuda a mantener modelos y auth alineados mientras te concentras en los bordes difíciles de la sincronización.

Finalmente, ejecuta un pequeño piloto. Elige una rebanada end‑to‑end de sincronización (por ejemplo, “enviar formulario de inspección con 1–2 fotos”) y lánzala con restricciones, reintentos y progreso visible al usuario funcionando completamente. Cuando esa rebanada sea aburrida y predecible, amplía una característica a la vez.

FAQ

¿Qué significa realmente “sincronización en segundo plano fiable” en una app de campo?

La sincronización fiable significa que el trabajo creado en el dispositivo se guarda primero localmente y se sube después sin que el usuario tenga que repetir pasos. Debe sobrevivir cierres de la app, reinicios, redes débiles y reintentos sin perder datos ni crear duplicados.

¿Cuándo debo usar OneTimeWorkRequest vs PeriodicWorkRequest para sincronización?

Usa trabajo one-time para todo lo que se active por un evento real como “formulario guardado”, “foto añadida” o cuando el usuario toca Sincronizar. Usa trabajo periódico para tareas de mantenimiento o como red de seguridad, pero no como única vía para subidas importantes porque su programación puede variar.

¿Qué tipo de Worker debería elegir: Worker, CoroutineWorker o RxWorker?

Si programas en Kotlin y tu código de sincronización usa funciones suspend, CoroutineWorker es la opción más simple y predecible, especialmente por la cancelación. Usa Worker sólo para tareas cortas y bloqueantes, y RxWorker únicamente si la app ya está basada en RxJava.

¿Debería encadenar varios workers o hacer todo en uno solo?

Encadena workers cuando los pasos tienen diferentes restricciones o deben reintentarse por separado (por ejemplo, subir archivos grandes por Wi‑Fi y luego hacer una llamada API pequeña en cualquier red). Usa un solo worker con fases claras cuando los pasos comparten estado y quieres comportamiento “todo o nada” para una sincronización lógica.

¿Cómo evito que los reintentos creen registros duplicados en el servidor?

Haz que cada petición de creación/actualización sea segura de repetir usando una clave de idempotencia por ítem (por ejemplo, un UUID guardado con el registro local). Si no puedes cambiar el servidor, usa upserts con claves estables o comprobaciones de versión para que repetir no cree registros nuevos.

¿Cómo hago que las subidas sean reanudables si la app se cierra a mitad de sincronización?

Persiste estados locales explícitos como queued, uploading, uploaded y failed para que el worker pueda reanudar sin adivinar. Marca un ítem como completado sólo después de la confirmación del servidor y guarda metadatos suficientes (URI del archivo, contador de intentos) para continuar tras un fallo o reinicio.

¿Qué restricciones son un buen valor por defecto para trabajos de sincronización en apps de campo?

Empieza con restricciones mínimas que protejan al usuario pero permitan que la sincronización ocurra la mayoría de los días: conexión de red requerida, evitar batería baja y evitar almacenamiento críticamente bajo. Ten cuidado con exigir “no medido” o “cargando”, pues pueden impedir que la sincronización ocurra en dispositivos de campo reales.

¿Cómo debería la app manejar portales cautivos o “Wi‑Fi sin internet”?

Trata “conectado pero sin internet” como un fallo normal: vence rápido el timeout, devuelve Result.retry() y vuelve a intentarlo más tarde. Si puedes detectarlo durante la petición, muestra un mensaje sencillo para que el usuario entienda por qué parece estar en línea pero la sincronización no avanza.

¿Cuál es la estrategia de reintentos más segura para redes inestables?

Usa retroceso exponencial para fallos de red para que los reintentos sean menos frecuentes cuando la cobertura es mala. Reintenta timeouts y errores 5xx, falla rápido en problemas permanentes como peticiones inválidas, y pon un límite de intentos para no buclear cuando el usuario debe actuar (por ejemplo, volver a iniciar sesión).

¿Cómo evito “tormentas de sincronización” y al mismo tiempo muestro progreso visible al usuario?

Encola la sincronización como trabajo único para que múltiples activadores no lancen trabajos en paralelo, y muestra un progreso confiable para subidas largas. Si el trabajo es de larga duración o iniciado por el usuario, ejecútalo en primer plano con una notificación continua que muestre conteos reales y ofrezca una opción clara de cancelar.

Fácil de empezar
Crea algo sorprendente

Experimente con AppMaster con plan gratuito.
Cuando esté listo, puede elegir la suscripción adecuada.

Empieza