Modèles de synchronisation en arrière‑plan avec Kotlin WorkManager pour applications sur le terrain
Modèles de synchronisation en arrière‑plan avec Kotlin WorkManager pour apps terrain : choisissez le bon type de travail, définissez des contraintes, utilisez un backoff exponentiel et affichez une progression visible.

Ce que signifie une synchronisation en arrière‑plan fiable pour les apps terrain et ops
Dans les apps terrain et ops, la sync n’est pas un "nice to have". C’est la façon dont le travail quitte l’appareil et devient concret pour l’équipe. Quand la sync échoue, les utilisateurs le remarquent vite : un travail terminé semble toujours « en attente », des photos disparaissent, ou le même rapport est envoyé deux fois et crée des doublons.
Ces apps sont plus difficiles que les apps grand public classiques parce que les téléphones opèrent dans les pires conditions. Le réseau bascule entre LTE, Wi‑Fi faible et pas de signal. Le mode économie d’énergie bloque le travail en arrière‑plan. L’app est tuée, le système se met à jour, et les appareils redémarrent en route. Une configuration WorkManager fiable doit survivre à tout cela sans drame.
« Finalement cohérent » signifie généralement quatre choses :
- Finalement cohérent : les données peuvent arriver en retard, mais elles arrivent sans surveillance manuelle.
- Récupérable : si l’app meurt en plein upload, l’exécution suivante continue en toute sécurité.
- Observable : les utilisateurs et le support peuvent savoir ce qui se passe et ce qui est bloqué.
- Non destructif : les retries ne créent pas de doublons ni n’altèrent l’état.
« Exécuter maintenant » convient aux petites actions déclenchées par l’utilisateur qui doivent finir rapidement (par exemple envoyer une mise à jour d’état avant que l’utilisateur ferme un travail). « Attendre » convient aux travaux plus lourds comme l’envoi de photos, des mises à jour par lot, ou tout ce qui risque d’épuiser la batterie ou d’échouer sur des réseaux médiocres.
Exemple : un inspecteur soumet un formulaire avec 12 photos dans un sous‑sol sans signal. Une sync fiable stocke tout localement, marque comme en file d’attente, et envoie plus tard quand l’appareil a une vraie connexion, sans que l’inspecteur refasse le travail.
Choisir les bons blocs de construction WorkManager
Commencez par choisir la plus petite unité de travail claire. Cette décision affecte la fiabilité plus que n’importe quelle logique de retry astucieuse par la suite.
OneTime vs Periodic
Utilisez OneTimeWorkRequest pour un travail qui doit se produire parce que quelque chose a changé : un nouveau formulaire sauvegardé, une photo finie de compresser, ou l’utilisateur a tapé Sync. Enqueuez‑le immédiatement (avec des contraintes) et laissez WorkManager l’exécuter quand l’appareil est prêt.
Utilisez PeriodicWorkRequest pour la maintenance régulière, comme une vérification de mises à jour ou un nettoyage nocturne. Le travail périodique n’est pas précis. Il a un intervalle minimum et peut dériver selon les règles de batterie et du système, donc il ne doit pas être votre seule voie pour les uploads importants.
Un schéma pratique est : travail one‑time pour « doit synchroniser bientôt », avec du périodique comme filet de sécurité.
Choisir Worker, CoroutineWorker, ou RxWorker
Si vous écrivez en Kotlin et utilisez des fonctions suspend, préférez CoroutineWorker. Il garde le code court et rend l’annulation prévisible.
Worker convient au code bloquant simple, mais attention à ne pas bloquer trop longtemps.
RxWorker a du sens seulement si votre app utilise déjà RxJava intensivement. Sinon c’est une complexité en plus.
Chaîner des étapes ou un worker avec des phases ?
Le chaînage est excellent quand les étapes peuvent réussir ou échouer indépendamment et que vous voulez des retries séparés et des logs clairs. Un worker unique avec des phases peut être meilleur quand les étapes partagent des données et doivent être traitées comme une transaction.
Une règle simple :
- Chaînez quand les étapes ont des contraintes différentes (upload sur Wi‑Fi seulement, puis un appel API léger).
- Utilisez un worker unique quand vous avez besoin d’une sync « tout‑ou‑rien ».
WorkManager garantit que le travail est persistant, peut survivre aux morts de processus et aux redémarrages, et respecte les contraintes. Il ne garantit pas un timing exact, une exécution immédiate, ni l’exécution après qu’un utilisateur ait forcé l’arrêt de l’app. Si vous construisez une app Android terrain (y compris une générée en Kotlin depuis AppMaster), concevez la sync pour que les délais soient sûrs et attendus.
Rendre la sync sûre : idempotente, incrémentale et reprise possible
Une app terrain relancera du travail. Les téléphones perdent le signal, le système tue les processus, et les utilisateurs tapent deux fois sur sync parce que rien ne semble se passer. Si votre sync en arrière‑plan n’est pas sûre à répéter, vous aurez des doublons, des mises à jour manquantes ou des retries infinis.
Commencez par rendre chaque appel serveur sûr à exécuter deux fois. L’approche la plus simple est une clé d’idempotence par élément (par exemple un UUID stocké avec l’enregistrement local) que le serveur traite comme « même requête, même résultat ». Si vous ne pouvez pas changer le serveur, utilisez une clé naturelle stable et un endpoint d’upsert, ou incluez un numéro de version pour que le serveur rejette les mises à jour obsolètes.
Suivez explicitement l’état local pour que le worker puisse reprendre après un crash sans deviner. Une machine à états simple suffit souvent :
- queued
- uploading
- uploaded
- needs-review
- failed-temporary
Gardez la sync incrémentale. Au lieu de « tout synchroniser », stockez un curseur comme lastSuccessfulTimestamp ou un token émis par le serveur. Lisez une petite page de changements, appliquez‑les, puis avancez le curseur seulement après que le lot est entièrement commité localement. De petits lots (20–100 éléments) réduisent les timeouts, rendent la progression visible et limitent la quantité de travail à répéter après une interruption.
Rendez les uploads résumables aussi. Pour les photos ou gros payloads, persistez l’URI du fichier et les métadonnées d’upload, et marquez comme uploadé seulement après confirmation serveur. Si le worker redémarre, il continue depuis le dernier état connu au lieu de tout recommencer.
Exemple : un technicien remplit 12 formulaires et joint 8 photos en sous‑sol. Quand l’appareil se reconnecte, le worker upload par lots, chaque formulaire a une clé d’idempotence, et le curseur de sync n’avance qu’après le succès de chaque lot. Si l’app est tuée à mi‑parcours, relancer le worker termine les éléments restants sans rien dupliquer.
Des contraintes adaptées aux conditions réelles des appareils
Les contraintes sont les garde‑fous qui empêchent la sync d’épuiser la batterie, de griller les forfaits data ou d’échouer au pire moment. Vous voulez des contraintes qui reflètent le comportement des appareils sur le terrain, pas celui de votre bureau.
Commencez par un petit ensemble qui protège les utilisateurs mais permet au travail de tourner la plupart des jours. Une base pratique : exiger une connexion réseau, éviter l’exécution quand la batterie est faible, et éviter l’exécution quand le stockage est critique. Ajoutez « en charge » seulement si le travail est lourd et pas urgent, car beaucoup d’appareils terrain sont rarement branchés pendant le service.
Sur‑contraindre est une raison fréquente des rapports « la sync ne se lance jamais ». Si vous exigez Wi‑Fi non mesuré, charge et batterie suffisante, vous demandez un moment parfait qui n’arrivera peut‑être jamais. Si le business a besoin des données aujourd’hui, mieux vaut exécuter du travail plus petit plus souvent que d’attendre des conditions idéales.
Les portails captifs sont un autre souci réel : le téléphone indique qu’il est connecté, mais l’utilisateur doit accepter une page captive. WorkManager ne peut pas détecter ça de façon fiable. Traitez‑le comme un échec normal : tentez la sync, expirez vite et réessayez plus tard. Affichez aussi un message in‑app simple comme « Connecté au Wi‑Fi mais pas d’accès internet » quand vous pouvez le détecter durant la requête.
Utilisez des contraintes différentes pour les petits et gros uploads afin que l’app reste réactive :
- Petits payloads (pings d’état, métadonnées de formulaire) : n’importe quel réseau, batterie pas faible.
- Gros payloads (photos, vidéos, packs de cartes) : réseau non mesuré quand possible, et envisager « en charge ».
Exemple : un technicien sauvegarde un formulaire avec 2 photos. Soumettez les champs du formulaire sur n’importe quelle connexion, mais mettez en file d’attente les photos pour Wi‑Fi ou un meilleur moment. Le bureau voit le travail rapidement, et l’appareil ne consomme pas de data mobile en arrière‑plan pour envoyer des images.
Retries avec backoff exponentiel sans exaspérer les utilisateurs
Les retries font qu’une app terrain paraît soit calme soit cassée. Choisissez une politique de backoff adaptée au type d’erreur attendu.
Le backoff exponentiel est généralement le défaut le plus sûr pour le réseau. Il augmente rapidement le délai d’attente pour éviter d’inonder le serveur ou d’épuiser la batterie quand la couverture est mauvaise. Le backoff linéaire peut convenir à des soucis temporaires (par exemple un VPN instable), mais il tend à réessayer trop souvent sur des zones à signal faible.
Fondez les décisions de retry sur le type d’échec, pas seulement sur « quelque chose a échoué ». Un petit ensemble de règles aide :
- Timeout réseau, 5xx, DNS, pas de connectivité :
Result.retry() - Auth expirée (401) : rafraîchir le token une fois, puis échouer et demander à l’utilisateur de se reconnecter
- Validation ou 4xx (bad request) :
Result.failure()avec une erreur claire pour le support - Conflit (409) pour des éléments déjà envoyés : traiter comme succès si votre sync est idempotente
Limitez les dégâts pour qu’une erreur permanente ne boucle pas indéfiniment. Fixez un nombre maximum de tentatives, et après cela, arrêtez et affichez un message unique et actionnable (pas des notifications répétées).
Vous pouvez aussi changer le comportement au fil des tentatives. Par exemple, après 2 échecs, envoyez des lots plus petits ou sautez les gros uploads jusqu’à la prochaine pull réussie.
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.build()
// in doWork()
if (runAttemptCount >= 5) return Result.failure()
return Result.retry()
Cela rend les retries polis : moins de réveils, moins d’interruptions pour l’utilisateur, et une récupération plus rapide quand la connexion revient.
Progression visible par l’utilisateur : notifications, travail au premier plan et statut
Les apps terrain synchronisent souvent quand l’utilisateur s’y attend le moins : dans un sous‑sol, sur un réseau lent, avec une batterie presque vide. Si la sync affecte ce que l’utilisateur attend (uploads, envoi de rapports, lots de photos), rendez‑la visible et facile à comprendre. Le travail silencieux en arrière‑plan est parfait pour de petites mises à jour rapides. Tout ce qui dure plus longtemps doit être honnête.
Quand le foreground est requis
Utilisez l’exécution au premier plan pour un job long, sensible au temps ou clairement lié à une action utilisateur. Sur Android moderne, de gros uploads peuvent être arrêtés ou retardés sauf si vous exécutez en foreground. Dans WorkManager, cela signifie retourner un ForegroundInfo pour que le système affiche une notification continue.
Une bonne notification répond à trois questions : qu’est‑ce qui sync, à quel point c’est avancé, et comment l’arrêter. Ajoutez une action d’annulation claire pour que l’utilisateur puisse stopper si son data est mesurée ou s’il a besoin de son téléphone maintenant.
Une progression en laquelle on peut avoir confiance
La progression doit correspondre à des unités réelles, pas des pourcentages vagues. Mettez à jour la progression avec setProgress et lisez‑la depuis WorkInfo dans votre UI (ou un écran de statut).
Si vous uploadez 12 photos et 3 formulaires, affichez « 5 sur 15 éléments envoyés », montrez ce qui reste, et gardez le dernier message d’erreur pour le support.
Gardez la progression significative :
- Éléments faits et éléments restants
- Étape courante ("Uploading photos", "Sending forms", "Finalizing")
- Dernière sync réussie
- Dernière erreur (courte, lisible par l’utilisateur)
- Option visible d’annuler/stopper
Si votre équipe utilise AppMaster pour produire des outils internes rapidement, gardez la même règle : les utilisateurs font confiance à la sync quand ils la voient et quand elle correspond à ce qu’ils essaient réellement d’accomplir.
Travail unique, tags et éviter les jobs en double
Les jobs de sync en double sont l’un des moyens les plus simples d’épuiser la batterie, de consommer de la data mobile et de créer des conflits côté serveur. WorkManager vous offre deux outils simples pour prévenir cela : des noms de travail uniques et des tags.
Un bon défaut est de traiter « sync » comme une seule voie. Au lieu d’enqueuer un nouveau job à chaque réveil de l’app, enfilez le même travail unique. Ainsi, vous n’aurez pas une tempête de sync quand l’utilisateur ouvre l’app, qu’un changement de réseau se produit et qu’un job périodique se déclenche en même temps.
val request = OneTimeWorkRequestBuilder<SyncWorker>()
.addTag("sync")
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork("sync", ExistingWorkPolicy.KEEP, request)
Choisir la politique est le choix comportemental principal :
KEEP: si une sync est déjà en cours (ou en file), ignorer la nouvelle requête. Utilisez‑le pour la plupart des boutons « Sync now » et des triggers auto‑sync.REPLACE: annuler l’actuel et démarrer frais. Utilisez‑le quand les inputs ont vraiment changé, comme un changement de compte ou de projet.
Les tags servent au contrôle et à la visibilité. Avec un tag stable comme sync, vous pouvez annuler, interroger le statut ou filtrer les logs sans suivre des IDs spécifiques. C’est particulièrement utile pour une action manuelle « sync now » : vous pouvez vérifier s’il y a déjà du travail en cours et afficher un message clair plutôt que de lancer un autre worker.
Le périodique et l’on‑demand ne doivent pas se battre. Gardez‑les séparés mais coordonnés :
- Utilisez
enqueueUniquePeriodicWork("sync_periodic", KEEP, ...)pour le job planifié. - Utilisez
enqueueUniqueWork("sync", KEEP, ...)pour l’on‑demand. - Dans votre worker, sortez vite s’il n’y a rien à envoyer ou recevoir, pour que la run périodique reste bon marché.
- Optionnellement, faites en sorte que le worker périodique enfile le même one‑time unique, pour que tout le travail réel se passe au même endroit.
Ces schémas rendent la sync prédictible : une sync à la fois, facile à annuler et facile à observer.
Pas à pas : un pipeline de synchronisation pratique
Un pipeline fiable est plus facile à construire si vous le traitez comme une petite machine à états : les éléments de travail vivent d’abord localement, et WorkManager ne fait qu’avancer leur état quand les conditions sont réunies.
Un pipeline simple que vous pouvez expédier
-
Commencez par des tables de « file » locales. Stockez le minimum de métadonnées nécessaires pour reprendre : id de l’élément, type (formulaire, photo, note), statut (pending, uploading, done), compteur de tentatives, dernière erreur, et un curseur ou révision serveur pour les téléchargements.
-
Pour un « Sync now » déclenché par l’utilisateur, enfilez un
OneTimeWorkRequestavec des contraintes adaptées au monde réel. Choix courants : réseau connecté et batterie pas faible. Si les uploads sont lourds, exigez aussi « en charge ». -
Implémentez un
CoroutineWorkerunique avec des phases claires : upload, download, reconcile. Gardez chaque phase incrémentale. Upload uniquement les éléments marqués pending, téléchargez seulement les changements depuis votre dernier curseur, puis résolvez les conflits avec des règles simples (par exemple : le serveur gagne pour les champs d’affectation, le client gagne pour les brouillons locaux). -
Ajoutez des retries avec backoff, mais soyez sélectif sur ce que vous réessayez. Timeouts et 500s doivent être réessayés. Un 401 (déconnexion) doit échouer vite et indiquer à l’UI ce qui s’est passé.
-
Observez
WorkInfopour piloter l’UI et les notifications. Utilisez des mises à jour de progression pour des phases comme « Uploading 3 of 10 », et affichez un court message d’échec qui pointe vers l’action suivante (réessayer, se reconnecter, se connecter au 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()
Quand vous gardez la file locale et les phases du worker explicites, vous obtenez un comportement prévisible : le travail peut se mettre en pause, reprendre et s’expliquer à l’utilisateur sans deviner ce qui s’est passé.
Erreurs courantes et pièges (et comment les éviter)
La fiabilité de la sync échoue le plus souvent à cause de quelques petits choix qui semblent inoffensifs en test, puis s’effondrent sur de vrais appareils. L’objectif n’est pas de synchroniser le plus souvent possible. C’est de synchroniser au bon moment, faire le bon travail, et s’arrêter proprement quand ce n’est pas possible.
Pièges à surveiller
- Faire de gros uploads sans contraintes. Si vous envoyez des photos ou de gros payloads sur n’importe quel réseau et avec n’importe quel niveau de batterie, les utilisateurs le ressentiront. Ajoutez des contraintes pour le type de réseau et la batterie, et découpez le travail en morceaux.
- Réessayer chaque erreur indéfiniment. Un 401, token expiré ou permission manquante n’est pas un problème temporaire. Marquez‑le comme échec dur, affichez une action claire (re‑login), et ne réessayez que les vrais problèmes transitoires comme les timeouts.
- Créer des doublons par accident. Si un worker peut tourner deux fois, votre serveur verra des créations en double à moins que les requêtes soient idempotentes. Utilisez un ID client stable par élément et faites en sorte que le serveur traite les répétitions comme des mises à jour.
- Utiliser du périodique pour des besoins quasi temps réel. Le périodique est pour la maintenance, pas pour « sync maintenant ». Pour la sync initiée par l’utilisateur, enfilez un travail unique et laissez l’utilisateur le déclencher quand il le souhaite.
- Afficher « 100% » trop tôt. La fin d’upload n’équivaut pas à l’acceptation et la réconciliation côté serveur. Suivez la progression par étapes (queued, uploading, server confirmed) et n’affichez terminé qu’après confirmation.
Un exemple concret : un technicien soumet un formulaire avec trois photos dans un ascenseur avec un signal faible. Si vous démarrez immédiatement sans contraintes, les uploads stagnent, les retries explosent, et le formulaire peut être créé deux fois au redémarrage de l’app. Si vous contraignez à un réseau utilisable, upload en étapes et attribuez à chaque formulaire un ID stable, la même situation se termine avec un seul enregistrement serveur propre et un message de progression fidèle.
Liste de vérification rapide avant la mise en production
Avant la release, testez la sync comme de vrais utilisateurs terrain la briseront : signal capricieux, batteries mortes et beaucoup de tapotements. Ce qui paraît correct sur un téléphone dev peut encore échouer en conditions réelles si la planification, les retries ou le reporting de statut sont bâclés.
Faites ces contrôles au moins sur un appareil lent et un appareil récent. Conservez les logs, mais regardez aussi ce que l’utilisateur voit dans l’UI.
- Pas de réseau, puis récupération : Lancez une sync sans connectivité, puis rallumez. Confirmez que le travail est mis en file (pas en échec rapide) et reprend sans dupliquer les uploads.
- Redémarrage de l’appareil : Commencez une sync, redémarrez à mi‑parcours, puis rouvrez l’app. Vérifiez que le travail continue ou se replanifie correctement, et que l’app affiche le bon état courant (pas bloquée sur "syncing").
- Batterie faible et stockage plein : Activez un mode économie, descendez sous le seuil de batterie faible si possible, et remplissez le stockage. Vérifiez que le job attend quand il le doit, puis continue une fois les conditions meilleures, sans boucler en retry qui vide la batterie.
- Déclenchements répétés : Tapez plusieurs fois sur votre bouton "Sync", ou déclenchez la sync depuis plusieurs écrans. Vous devez toujours aboutir à une seule exécution logique, pas une pile de workers parallèles qui se battent pour les mêmes enregistrements.
- Pannes serveur expliquables : Simulez 500s, timeouts et erreurs d’auth. Vérifiez que les retries reculent et s’arrêtent après un plafond, et que l’utilisateur voit un message clair comme "Impossible de joindre le serveur, réessaiera" plutôt qu’un échec générique.
Si un test laisse l’app dans un état peu clair, considérez‑le comme un bug. Les utilisateurs pardonnent une sync lente, mais pas la perte de données ni l’incertitude.
Scénario exemple : formulaires hors ligne et uploads de photos
Un technicien arrive sur site avec une couverture faible. Il remplit un formulaire hors ligne, capture 12 photos et appuie sur Submit avant de partir. L’app sauvegarde tout localement (par ex. dans une base locale) : un enregistrement pour le formulaire, et un enregistrement par photo avec un état clair comme PENDING, UPLOADING, DONE ou FAILED.
Lorsqu’il appuie sur Submit, l’app enfile un job de sync unique pour éviter les doublons s’il tape deux fois. Une configuration courante est une chaîne WorkManager qui upload d’abord les photos (plus grosses, plus lentes), puis envoie la charge du formulaire après confirmation des pièces jointes.
La sync ne tourne que quand les conditions correspondent au terrain réel : par exemple, réseau connecté, batterie pas faible et stockage suffisant. Si le technicien est encore dans le sous‑sol sans signal, rien ne brûle la batterie en boucle.
La progression est évidente et conviviale. L’upload fonctionne en foreground et affiche une notification « Uploading 3 of 12 », avec une action Annuler claire. Si l’utilisateur annule, l’app arrête le travail et laisse les éléments restants en PENDING pour pouvoir réessayer plus tard sans perdre de données.
Les retries restent polis après un hotspot instable : le premier échec réessaie vite, puis chaque échec augmente l’attente (backoff exponentiel). Au début, ça paraît réactif, puis ça s’espace pour éviter d’épuiser la batterie et de spammer le réseau.
Pour l’équipe ops, le bénéfice est concret : moins de soumissions en double parce que les éléments sont idempotents et mis en file de façon unique, des états d’échec clairs (quelle photo a échoué, pourquoi, et quand elle réessaiera), et une meilleure confiance que « soumis » signifie « sauvegardé en sûr et en attente de sync ».
Prochaines étapes : livrez la fiabilité d’abord, puis élargissez la portée de la sync
Avant d’ajouter plus de fonctionnalités de sync, définissez clairement ce que « terminé » signifie. Pour la plupart des apps terrain, ce n’est pas « requête envoyée ». C’est « le serveur a accepté et confirmé », plus un état UI qui reflète la réalité. Un formulaire affichant « Synced » doit le rester après un redémarrage, et un formulaire échoué doit indiquer quoi faire ensuite.
Rendez l’app facile à faire confiance en ajoutant un petit ensemble de signaux visibles que les gens et le support peuvent vérifier. Gardez‑les simples et constants :
- Dernière sync réussie
- Dernière erreur de sync (message court, pas de stack trace)
- Éléments en attente (par ex. : 3 formulaires, 12 photos)
- État courant de la sync (Idle, Syncing, Needs attention)
Traitez l’observabilité comme une partie de la fonctionnalité. Cela fait gagner des heures sur le terrain quand quelqu’un est sur une connexion faible et ne sait pas si l’app fonctionne.
Si vous construisez aussi le backend et les outils admin, les générer ensemble aide à garder le contrat de sync stable. AppMaster (appmaster.io) peut générer un backend prêt pour la production, un panneau admin web et des apps mobiles natives, ce qui aide à garder modèles et auth alignés pendant que vous vous concentrez sur les bords délicats de la sync.
Enfin, faites un petit pilote. Choisissez une tranche de bout en bout (par ex. « soumettre formulaire d’inspection avec 1–2 photos ») et publiez‑la avec contraintes, retries et progression visible entièrement fonctionnels. Quand cette tranche devient ennuyeuse et prévisible, étendez‑une fonctionnalité à la fois.
FAQ
La synchronisation fiable signifie que le travail créé sur l’appareil est d’abord sauvegardé localement et sera envoyé plus tard sans que l’utilisateur ait à répéter les actions. Elle doit survivre aux fermetures d’app, redémarrages, réseaux faibles et tentatives répétées sans perdre de données ni créer des doublons.
Utilisez un travail one-time pour tout ce qui est déclenché par un événement réel comme « formulaire sauvegardé », « photo ajoutée » ou un utilisateur qui tape sur Sync. Utilisez du travail périodique pour la maintenance et comme filet de sécurité, mais pas comme unique mécanisme pour des envois importants, car son timing peut dériver.
Si vous êtes en Kotlin et que votre code de sync utilise des fonctions suspend, CoroutineWorker est le choix le plus simple et le plus prévisible, surtout pour la gestion de l’annulation. Utilisez Worker seulement pour des tâches bloquantes courtes, et RxWorker uniquement si l’app utilise déjà massivement RxJava.
Chaînez des workers quand les étapes ont des contraintes différentes ou doivent être réessayées séparément (par exemple : upload lourd sur Wi‑Fi, puis appel API léger sur n’importe quel réseau). Utilisez un seul worker avec des phases claires quand les étapes partagent des données et que vous voulez un comportement « tout ou rien » pour une même opération logique.
Rendez chaque requête de création/mise à jour sûre à répéter en utilisant une clé d’idempotence par élément (souvent un UUID stocké avec l’enregistrement local). Si vous ne pouvez pas changer le serveur, utilisez des upserts avec des clés stables ou des contrôles de version pour que les répétitions n’engendrent pas de nouvelles lignes.
Persistez des statuts locaux explicites comme queued, uploading, uploaded et failed pour que le worker puisse reprendre sans deviner. Ne marquez un élément comme terminé qu’après confirmation du serveur, et enregistrez assez de métadonnées (URI du fichier, nombre de tentatives) pour continuer après un crash ou un redémarrage.
Commencez par des contraintes minimales qui protègent les utilisateurs mais laissent la sync fonctionner la plupart du temps : réseau requis, éviter batterie faible et éviter stockage critique. Faites attention aux exigences « non mesuré » ou « en charge », car elles risquent de faire en sorte que la sync ne se lance jamais sur de nombreux appareils terrain.
Considérez « connecté mais pas d’internet » comme une erreur normale : expirer rapidement, retourner Result.retry() et réessayer plus tard. Si vous pouvez le détecter pendant la requête, affichez un message simple pour que l’utilisateur comprenne pourquoi la connexion semble active mais que la sync ne progresse pas.
Pour les pannes réseau, utilisez un backoff exponentiel pour que les tentatives deviennent moins fréquentes quand la couverture est mauvaise. Réessayez timeouts et erreurs 5xx, échouez rapidement sur les problèmes permanents (requêtes invalides), et limitez le nombre d’essais pour ne pas boucler indéfiniment quand l’intervention de l’utilisateur est nécessaire (par exemple se reconnecter).
Enfilez la sync comme un travail unique (unique work) pour éviter que plusieurs déclenchements lancent des jobs parallèles, et affichez une progression dont les utilisateurs peuvent se fier pour les uploads longs. Si le travail est long ou initié par l’utilisateur, exécutez‑le au premier plan (foreground) avec une notification persistante montrant des comptes réels et une option claire d’annulation.


