Kotlin WorkManager achtergrond-synchronisatiepatronen voor veldapps
Kotlin WorkManager achtergrond-synchronisatiepatronen voor veldapps: kies het juiste work-type, stel passende constraints in, gebruik exponentiële backoff en toon zichtbare voortgang voor gebruikers.

Wat betrouwbare achtergrond-synchronisatie betekent voor veld- en operations-apps
In veld- en ops-apps is synchronisatie geen "nice to have". Het is hoe werk het apparaat verlaat en echt wordt voor het team. Wanneer synchronisatie faalt, merken gebruikers dat snel: een afgeronde taak ziet er nog steeds als “in wachtrij” uit, foto's verdwijnen, of hetzelfde rapport uploadt twee keer en creëert duplicaten.
Deze apps zijn lastiger dan typische consumentenapps omdat telefoons in de slechtste omstandigheden werken. Netwerk wisselt tussen LTE, zwakke Wi‑Fi en geen signaal. Energiebesparing blokkeert achtergrondwerk. De app wordt gedood, het OS wordt bijgewerkt en apparaten herstarten halverwege een route. Een betrouwbare WorkManager-configuratie moet dat allemaal overleven zonder drama.
Betrouwbaar betekent meestal vier dingen:
- Eventually consistent: data kan vertraagd aankomen, maar het komt zonder handmatig nakijken.
- Recoverable: als de app middenin upload crasht, gaat de volgende run veilig verder.
- Observable: gebruikers en support kunnen zien wat er gebeurt en wat vastzit.
- Non-destructive: retries creëren geen duplicaten of corrupte status.
"Run now" past bij kleine, door de gebruiker getriggerde acties die snel klaar moeten zijn (bijv. het verzenden van een enkele statusupdate voordat de gebruiker een taak sluit). "Wait" past bij zwaarder werk zoals foto-uploads, batch-updates of alles wat de batterij kan legen of kan falen op slechte netwerken.
Voorbeeld: een inspecteur dient een formulier in met 12 foto's in een kelder zonder signaal. Een betrouwbare synchronisatie slaat alles lokaal op, markeert het als in wachtrij en uploadt later wanneer het apparaat een echte verbinding heeft, zonder dat de inspecteur het werk opnieuw hoeft te doen.
Kies de juiste WorkManager bouwblokken
Begin met het kiezen van de kleinste, meest duidelijke eenheid van werk. Die beslissing beïnvloedt betrouwbaarheid meer dan welke slimme retry-logica dan ook.
One-time vs periodic work
Gebruik OneTimeWorkRequest voor werk dat moet gebeuren omdat er iets veranderde: een nieuw formulier werd opgeslagen, een foto klaar was met comprimeren of de gebruiker op Sync tikte. Zet het meteen in de wachtrij (met constraints) en laat WorkManager het uitvoeren wanneer het apparaat er klaar voor is.
Gebruik PeriodicWorkRequest voor regelmatig onderhoud, zoals een "check for updates" pull of een nachtelijke cleanup. Periodiek werk is niet exact. Het heeft een minimale interval en kan schuiven op basis van batterij- en systeemregels, dus het mag niet je enige route zijn voor belangrijke uploads.
Een praktisch patroon is: one-time work voor "moet snel synchroniseren", met periodiek werk als vangnet.
Kiezen tussen Worker, CoroutineWorker of RxWorker
Als je Kotlin gebruikt en suspend-functies hebt, geef dan de voorkeur aan CoroutineWorker. Het houdt de code kort en laat annulering zich gedragen zoals je verwacht.
Worker past bij eenvoudige blocking code, maar wees voorzichtig om niet te lang te blokkeren.
RxWorker heeft alleen zin als je app al zwaar op RxJava draait. Anders is het extra complexiteit.
Ketenen van stappen of één worker met fases?
Keten is geweldig wanneer stappen onafhankelijk kunnen slagen of falen en je aparte retries en duidelijkere logs wilt. Eén worker met fases kan beter zijn wanneer stappen data delen en als één transactie behandeld moeten worden.
Een eenvoudige regel:
- Ketens wanneer stappen verschillende constraints hebben (Wi‑Fi-only upload en daarna een lichte API-aanroep).
- Gebruik één worker wanneer je één “all-or-nothing” synchronisatie nodig hebt.
WorkManager garandeert dat werk persistente opslag heeft, processen overleeft en reboots respecteert, en constraints naleeft. Het garandeert geen exacte timing, onmiddellijke uitvoering of draaien nadat de gebruiker de app force-stopt. Als je een Android-veldapp bouwt (ook een die als Kotlin is gegenereerd uit AppMaster), ontwerp synchronisatie zodat vertragingen veilig en verwacht zijn.
Maak synchronisatie veilig: idempotent, incrementeel en hervatbaar
Een veldapp zal werk opnieuw uitvoeren. Telefoons verliezen signaal, het OS dwingt processen te stoppen en gebruikers tikken soms twee keer op synchroniseren omdat niets leek te gebeuren. Als je achtergrondsynchronisatie niet veilig is om te herhalen, krijg je dubbele records, ontbrekende updates of eindeloze retries.
Begin met elke serveraanroep veilig te maken om twee keer te draaien. De eenvoudigste aanpak is een idempotency-key per item (bijv. een UUID dat bij het lokale record wordt opgeslagen) die de server als "zelfde verzoek, zelfde resultaat" behandelt. Als je de server niet kunt aanpassen, gebruik dan een stabiele natuurlijke sleutel en een upsert-endpoint, of voeg een versienummer toe zodat de server verouderde updates kan weigeren.
Volg lokale status expliciet zodat de worker na een crash kan hervatten zonder te raden. Een eenvoudige toestandsmachine is vaak genoeg:
- queued
- uploading
- uploaded
- needs-review
- failed-temporary
Houd synchronisatie incrementeel. In plaats van "alles synchroniseren", sla een cursor op zoals lastSuccessfulTimestamp of een server-issued token. Lees een kleine pagina wijzigingen, pas ze toe en verplaats de cursor alleen nadat de batch volledig lokaal is vastgelegd. Kleine batches (zoals 20–100 items) verminderen timeouts, maken voortgang zichtbaar en beperken hoeveel werk je moet herhalen na een onderbreking.
Maak uploads ook hervatbaar. Voor foto's of grote payloads, bewaar de file URI en upload-metadata, en markeer pas als geüpload nadat de server bevestigt. Als de worker opnieuw start, gaat hij verder vanaf de laatst bekende status in plaats van opnieuw te beginnen.
Voorbeeld: een technicus vult 12 formulieren in en voegt 8 foto's toe ondergronds. Wanneer het apparaat opnieuw verbinding heeft, uploadt de worker in batches, elk formulier heeft een idempotency-key en de synchronisatiecursor schuift alleen nadat elke batch slaagt. Als de app halverwege wordt gedood, voltooit het opnieuw draaien van de worker de resterende items zonder iets te dupliceren.
Constraints die overeenkomen met echte apparaatcondities
Constraints zijn de vangrails die achtergrondsynchronisatie weghouden van het legen van batterijen, het verbranden van databundels of falen op het slechtste moment. Je wilt constraints die weerspiegelen hoe apparaten in het veld werken, niet hoe ze op je bureau werken.
Begin met een kleine set die gebruikers beschermt maar het werk nog steeds de meeste dagen laat draaien. Een praktisch basisniveau is: vereis een netwerkverbinding, voorkom draaien bij lage batterij en voorkom draaien bij kritiek lage opslag. Voeg "opladen" alleen toe als het werk zwaar en niet tijdkritisch is, want veel veldapparaten worden tijdens shifts zelden aangesloten.
Over-constraining is een veelvoorkomende reden voor "synchronisatie draait nooit"-meldingen. Als je unmetered Wi‑Fi, opladen en batterij niet laag vereist, vraag je in feite om een perfect moment dat misschien nooit voorkomt. Als het zakelijk gezien vandaag data nodig is, is het beter om kleiner werk vaker te draaien dan te wachten op ideale omstandigheden.
Captive portals zijn een ander realistisch probleem: de telefoon zegt dat hij verbonden is, maar de gebruiker moet op "Akkoord" tikken op een hotel- of publieke Wi‑Fi-pagina. WorkManager kan die staat niet betrouwbaar detecteren. Behandel het als een normale fout: probeer de synchronisatie, time out snel en probeer het later opnieuw. Toon ook waar mogelijk een eenvoudige in-app melding zoals "Verbonden met Wi‑Fi maar geen internet" als je het tijdens het verzoek kunt detecteren.
Gebruik verschillende constraints voor kleine versus grote uploads zodat de app responsief blijft:
- Kleine payloads (status pings, formuliermetadata): elk netwerk, batterij niet laag.
- Grote payloads (foto's, video's, kaartpakketten): bij voorkeur onbetaalde verbinding en overweeg opladen.
Voorbeeld: een technicus slaat een formulier op met 2 foto's. Verstuur de formuliergegevens op elke verbinding, maar zet foto-uploads in de wachtrij om te wachten op Wi‑Fi of een beter moment. Het kantoor ziet de taak snel en het apparaat verbruikt geen mobiele data om afbeeldingen op de achtergrond te uploaden.
Retries met exponentiële backoff die gebruikers niet irriteren
Retries zijn het punt waarop veldapps ofwel kalm aanvoelen of kapot lijken. Kies een backoff-beleid dat past bij het soort fout dat je verwacht.
Exponentiële backoff is meestal de veiligste standaard voor netwerken. Het vergroot snel de wachttijd zodat je de server niet blijft belagen of de batterij niet blijft leeg trekken wanneer dekking slecht is. Lineaire backoff kan passen bij korte, tijdelijke problemen (bijv. een wankele VPN), maar het probeert vaak te vaak opnieuw in gebieden met zwak signaal.
Maak retry-beslissingen op basis van het fouttype, niet alleen "er ging iets mis". Een eenvoudige regelset helpt:
- Netwerktimeout, 5xx, DNS, geen connectiviteit:
Result.retry() - Auth verlopen (401): vernieuw het token één keer, faal daarna en vraag de gebruiker om opnieuw in te loggen
- Validatie of 4xx (bad request):
Result.failure()met een duidelijke fout voor support - Conflict (409) voor reeds-verzonden items: behandel als succes als je synchronisatie idempotent is
Beperk de schade zodat een permanente fout niet eeuwig blijft loopen. Stel een maximaal aantal pogingen in en stop daarna en toon een stille, actiegerichte melding (geen herhaalde notificaties).
Je kunt ook gedrag veranderen naarmate pogingen toenemen. Bijvoorbeeld: na 2 mislukkingen, stuur kleinere batches of sla grote uploads over totdat de volgende succesvolle pull plaatsvindt.
val request = OneTimeWorkRequestBuilder\u003cSyncWorker\u003e()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.build()
// in doWork()
if (runAttemptCount \u003e= 5) return Result.failure()
return Result.retry()
Dit houdt retries beleefd: minder wakeups, minder gebruikersinterrupties en snellere herstel wanneer de verbinding terugkeert.
Voor gebruikers zichtbare voortgang: notificaties, foreground werk en status
Veldapps synchroniseren vaak wanneer de gebruiker het het minst verwacht: in een kelder, op een traag netwerk, met bijna lege batterij. Als synchronisatie invloed heeft op waar de gebruiker op wacht (uploads, rapporten verzenden, fotobatches), maak het zichtbaar en makkelijk te begrijpen. Stilte op de achtergrond is prima voor kleine, snelle updates. Alles wat langer duurt, moet eerlijk aanvoelen.
Wanneer foreground-werk nodig is
Gebruik foreground-executie wanneer een taak langloopt, tijdsgevoelig is of duidelijk gekoppeld is aan een gebruikersactie. Op moderne Android kunnen grote uploads worden gestopt of vertraagd tenzij je als foreground draait. In WorkManager betekent dat het teruggeven van een ForegroundInfo zodat het systeem een lopende notificatie toont.
Een goede notificatie beantwoordt drie vragen: wat synchroniseert, hoe ver is het, en hoe stop je het. Voeg een duidelijke annuleeractie toe zodat de gebruiker kan afhaken als ze op gemeten data zitten of het toestel nu nodig hebben.
Voortgang waarop mensen kunnen vertrouwen
Voortgang moet naar echte eenheden verwijzen, geen vage percentages. Update voortgang met setProgress en lees het uit WorkInfo in je UI (of een statuspagina).
Als je 12 foto's en 3 formulieren uploadt, rapporteer dan "5 van 15 items geüpload", toon wat er nog over is en bewaar het laatste foutbericht voor support.
Houd voortgang zinvol:
- Items gedaan en items resterend
- Huidige stap ("Foto's uploaden", "Formulieren verzenden", "Afronden")
- Laatste succesvolle synchronisatietijd
- Laatste fout (kort, gebruiksvriendelijk)
- Een zichtbare annuleer/stop-optie
Als je team intern snel tools bouwt met AppMaster, houd dan dezelfde regel: gebruikers vertrouwen synchronisatie wanneer ze het kunnen zien en wanneer het overeenkomt met wat ze daadwerkelijk proberen te doen.
Unieke work, tags en het vermijden van dubbele sync-jobs
Dubbele sync-jobs zijn een van de makkelijkste manieren om batterij te legen, mobiele data te verbranden en server-side conflicts te creëren. WorkManager geeft twee simpele tools om dat te voorkomen: unieke work-namen en tags.
Een goede standaard is om "sync" als een enkele lane te behandelen. In plaats van elke keer een nieuwe job in de wachtrij te plaatsen wanneer de app wakker wordt, enqueue je dezelfde unieke work-naam. Zo krijg je geen sync-storm wanneer de gebruiker de app opent, een netwerkverandering optreedt en een periodieke job tegelijk afgaat.
val request = OneTimeWorkRequestBuilder\u003cSyncWorker\u003e()
.addTag("sync")
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork("sync", ExistingWorkPolicy.KEEP, request)
Het kiezen van het policy is de belangrijkste gedrag-keuze:
KEEP: als een sync al draait (of in de wachtrij staat), negeer het nieuwe verzoek. Gebruik dit voor de meeste "Sync nu"-knoppen en auto-sync triggers.REPLACE: annuleer de huidige en start opnieuw. Gebruik dit wanneer de inputs echt veranderden, zoals de gebruiker van account wisselde of een ander project selecteerde.
Tags zijn je handvat voor controle en zichtbaarheid. Met een stabiele tag zoals sync kun je annuleren, status opvragen of logs filteren zonder specifieke IDs bij te houden. Dit is vooral handig voor een handmatige "sync nu"-actie: je kunt checken of er al werk draait en een duidelijke melding tonen in plaats van nog een worker te starten.
Periodieke en on-demand synchronisatie moeten elkaar niet tegenwerken. Houd ze gescheiden, maar gecoördineerd:
- Gebruik
enqueueUniquePeriodicWork("sync_periodic", KEEP, ...)voor de geplande job. - Gebruik
enqueueUniqueWork("sync", KEEP, ...)voor on-demand. - Laat je worker snel stoppen als er niets te uploaden of downloaden is, zodat de periodieke run goedkoop blijft.
- Optioneel: laat de periodieke worker dezelfde one-time unieke sync enqueue-en, zodat al het echte werk op één plek gebeurt.
Deze patronen houden achtergrondsynchronisatie voorspelbaar: één sync tegelijk, makkelijk te annuleren en eenvoudig te observeren.
Stapsgewijs: een praktische background-sync pipeline
Een betrouwbare synchronisatiepipeline is makkelijker te bouwen wanneer je het als een kleine toestandsmachine behandelt: werkitems leven eerst lokaal en WorkManager verplaatst ze alleen verder wanneer de condities kloppen.
Een eenvoudige pipeline die je kunt uitrollen
-
Begin met lokale "queue"-tabellen. Sla de kleinste metadata op die je nodig hebt om te hervatten: item-id, type (formulier, foto, notitie), status (pending, uploading, done), pogingsteller, laatste fout en een cursor of server-revisie voor downloads.
-
Voor een door de gebruiker getriggerde "Sync now", enqueue een
OneTimeWorkRequestmet constraints die overeenkomen met de echte wereld. Veelvoorkomende keuzes zijn netwerk verbonden en batterij niet laag. Als uploads zwaar zijn, vereist dan ook opladen. -
Implementeer één
CoroutineWorkermet duidelijke fases: upload, download, reconcile. Houd elke fase incrementeel. Upload alleen items gemarkeerd als pending, download alleen wijzigingen sinds je laatste cursor en reconcile conflicts met eenvoudige regels (bijv. server wint voor toewijzingsvelden, client wint voor lokale conceptnotities). -
Voeg retries met backoff toe, maar wees selectief over wat je retryt. Time-outs en 500s moeten retryen. Een 401 (uitgelogd) moet snel falen en de UI vertellen wat er gebeurde.
-
Observeer
WorkInfoom UI en notificaties aan te sturen. Gebruik voortgangsupdates voor fases zoals "Uploading 3 of 10" en toon een korte foutmelding die naar de volgende actie wijst (retry, inloggen, verbinden met Wi‑Fi).
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = OneTimeWorkRequestBuilder\u003cSyncWorker\u003e()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
Als je de wachtrij lokaal houdt en de worker-fases expliciet zijn, krijg je voorspelbaar gedrag: werk kan pauzeren, hervatten en zichzelf aan de gebruiker uitleggen zonder te raden wat er gebeurde.
Veelgemaakte fouten en valkuilen (en hoe ze te vermijden)
Betrouwbare synchronisatie faalt meestal door een paar kleine keuzes die onschuldig lijken tijdens testing, maar in het veld uit elkaar vallen. Het doel is niet om zo vaak mogelijk te draaien. Het doel is om op het juiste moment het juiste werk te doen en netjes te stoppen wanneer het niet kan.
Valkuilen om op te letten
- Grote uploads doen zonder constraints. Als je foto's of grote payloads op elk netwerk en bij elke batterijstand pusht, voelen gebruikers dat. Voeg constraints toe voor netwerktype en lage batterij en split groot werk in kleinere stukken.
- Elke fout voor altijd retryen. Een 401, verlopen token of ontbrekende permissie is geen tijdelijk probleem. Markeer het als een harde fout, geef een duidelijke actie (opnieuw inloggen) en retry alleen echte transients zoals timeouts.
- Per ongeluk duplicaten creëren. Als een worker twee keer kan draaien, ziet je server dubbele creates tenzij verzoeken idempotent zijn. Gebruik een stabiel client-gegenereerd ID per item en laat de server herhalingen als updates behandelen, niet als nieuwe records.
- Periodiek werk gebruiken voor near real-time behoeften. Periodiek werk is het beste voor onderhoud, niet voor "nu synchroniseren". Voor gebruikersgestuurde sync enqueue je one-time unieke work en laat je de gebruiker het triggeren wanneer dat nodig is.
- "100%" te vroeg rapporteren. Upload voltooiing is niet hetzelfde als data geaccepteerd en gereconcilieerd. Volg voortgang in stadia (queued, uploading, server bevestigd) en toon pas "klaar" na bevestiging.
Een concreet voorbeeld: een technicus dient een formulier met drie foto's in een lift met zwak signaal. Als je meteen start zonder constraints, stagneren uploads, spikes in retries ontstaan en het formulier kan twee keer worden aangemaakt wanneer de app herstart. Als je constrain tot bruikbaar netwerk, in stappen uploadt en elk formulier een stabiele ID geeft, eindigt hetzelfde scenario met één net record op de server en een eerlijke voortgangsmelding.
Snelle checklist voordat je live gaat
Test synchronisatie zoals echte veldgebruikers het zullen breken: zwak signaal, lege batterijen en veel tikken. Wat op een dev-telefoon goed lijkt, kan nog steeds falen in het wild als scheduling, retries of statusrapportage niet kloppen.
Draai deze checks op minstens één langzaam apparaat en één nieuwer apparaat. Houd logs, maar let ook op wat de gebruiker ziet in de UI.
- No netwerk, daarna herstel: Start een sync zonder connectiviteit en zet het daarna aan. Bevestig dat werk in de wachtrij staat (niet snel faalt) en later hervat zonder uploads te dupliceren.
- Apparaat herstart: Begin een sync, herstart halverwege en open de app opnieuw. Verifieer dat het werk doorgaat of opnieuw gepland wordt en dat de app de juiste huidige status toont (niet vast op "synchroniseren").
- Lage batterij en weinig opslag: Zet energiebesparing aan, daal onder de drempel voor lage batterij indien mogelijk en vul opslag bijna vol. Bevestig dat de job wacht wanneer dat moet en vervolgens doorgaat wanneer condities verbeteren, zonder batterij te verbranden in een retry-loop.
- Herhaalde triggers: Tik meerdere keren op je "Sync"-knop of trigger sync vanaf meerdere schermen. Je moet nog steeds uitkomen op één logische synchronisatierun, niet een stapel parallelle workers die met dezelfde records concurreren.
- Serverfouten die je kunt uitleggen: Simuleer 500s, timeouts en auth-fouten. Controleer dat retries backoffen en stoppen na een cap, en dat de gebruiker een duidelijke melding ziet zoals "Kan server niet bereiken, probeert later opnieuw" in plaats van een generieke fout.
Als een test de app in een onduidelijke staat laat, beschouw dat als een bug. Gebruikers vergeven trage synchronisatie, maar ze vergeven het niet dat data verloren gaat of dat ze niet weten wat er gebeurde.
Voorbeeldscenario: offline formulieren en foto-uploads in een veldapp
Een technicus arriveert op een locatie met zwakke dekking. Ze vullen offline een serviceformulier in, maken 12 foto's en tikken op Verzenden voordat ze vertrekken. De app slaat eerst alles lokaal op (bijv. in een lokale database): één record voor het formulier en één record per foto met een duidelijke status zoals PENDING, UPLOADING, DONE of FAILED.
Wanneer ze op Verzenden tikken, enqueueert de app een unieke synchronisatiejob zodat er geen duplicaten ontstaan als ze twee keer tikken. Een veelvoorkomende opzet is een WorkManager-keten die eerst foto's uploadt (groter, langzamer) en daarna de formuliervelden verzendt nadat bijlagen bevestigd zijn.
De synchronisatie draait alleen wanneer de condities overeenkomen met het echte leven. Bijvoorbeeld: het wacht op een verbonden netwerk, een niet-laag batterijniveau en voldoende opslag. Als de technicus nog in de kelder zonder signaal zit, verbrandt er niets in een achtergrondloop.
De voortgang is duidelijk en gebruiksvriendelijk. De upload draait als foreground work en toont een notificatie zoals "Uploading 3 of 12", met een duidelijke Annuleer-optie. Als ze annuleren, stopt de app het werk en houdt de resterende items in PENDING zodat ze later opnieuw geprobeerd kunnen worden zonder data te verliezen.
Retries gedragen zich beleefd na een wankele hotspot: de eerste fout retryt snel, maar elke volgende fout wacht langer (exponentiële backoff). Het voelt eerst responsief en gaat daarna terugschakelen om batterij te sparen en het netwerk niet te spammen.
Voor het ops-team is de opbrengst praktisch: minder dubbele inzendingen omdat items idempotent en uniek in de wachtrij staan, duidelijke foutstaten (welke foto faalde, waarom en wanneer het opnieuw geprobeerd wordt) en meer vertrouwen dat "verzonden" ook echt betekent "veilig opgeslagen en zal synchroniseren".
Volgende stappen: lever betrouwbaarheid eerst, breid dan het synchronisatiebereik uit
Voeg niet te snel meer synchronisatiefuncties toe; wees helder over wat "klaar" betekent. Voor de meeste veldapps is dat niet "verzoek verzonden" maar "server heeft geaccepteerd en bevestigd", plus een UI-status die overeenkomt met de realiteit. Een formulier dat "Gesynchroniseerd" toont, moet zo blijven na een app-herstart, en een mislukt formulier moet tonen wat de volgende stap is.
Maak de app gemakkelijk te vertrouwen door een klein setje signalen zichtbaar te maken (en waar support naar kan vragen). Houd ze simpel en consistent over schermen:
- Laatste succesvolle synchronisatietijd
- Laatste synchronisatiefout (korte melding, geen stack trace)
- Items in wachtrij (bijv.: 3 formulieren, 12 foto's)
- Huidige synchronisatiestatus (Idle, Syncing, Needs attention)
Behandel observeerbaarheid als onderdeel van de feature. Het bespaart uren in het veld wanneer iemand op een zwakke verbinding zit en niet weet of de app werkt.
Als je ook backend en admin-tools bouwt, helpt het om die samen te genereren zodat het synchronisatiecontract stabiel blijft. AppMaster (appmaster.io) kan een productieklare backend, een web adminpaneel en native mobiele apps genereren, wat kan helpen om modellen en auth op één lijn te houden terwijl jij je richt op de lastige sync-randen.
Tot slot: voer een kleine pilot uit. Kies één end-to-end synchronisatiedeel (bijv. "inspectieformulier indienen met 1–2 foto's") en lever het met constraints, retries en gebruikerszichtbare voortgang volledig werkend. Als dat onderdeel saai en voorspelbaar is, breid dan één feature tegelijk uit.
FAQ
Betrouwbare achtergrond-synchronisatie betekent dat werk dat op het apparaat is aangemaakt eerst lokaal wordt opgeslagen en later geüpload wordt zonder dat de gebruiker stappen hoeft te herhalen. Het moet app-kills, herstarts, zwakke netwerken en retries overleven zonder data te verliezen of duplicaten te maken.
Gebruik one-time werk voor alles wat door een echt evenement wordt getriggerd, zoals “formulier opgeslagen”, “foto toegevoegd” of de gebruiker die op Sync tikt. Gebruik periodiek werk voor onderhoud en als vangnet, maar niet als enige pad voor belangrijke uploads omdat de timing kan afwijken.
Als je in Kotlin werkt en je synchronisatiecode gebruikt suspend-functies, is CoroutineWorker de eenvoudigste en meest voorspelbare keuze, vooral vanwege annulering. Gebruik Worker alleen voor korte blocking taken en RxWorker alleen als de app al sterk op RxJava is gebouwd.
Keten van workers is handig wanneer stappen verschillende constraints hebben of afzonderlijk moeten kunnen falen en retryen (bijv. foto-upload via Wi‑Fi en daarna een kleine API-aanroep). Gebruik één worker met duidelijke fases wanneer stappen staat delen en je “alles-of-niets” gedrag wilt voor één logische synchronisatierun.
Zorg dat elke create/update-aanvraag veilig twee keer kan draaien door een idempotency-key per item te gebruiken (vaak een UUID dat in het lokale record staat). Als je de server niet kunt veranderen, streef dan naar upserts met stabiele sleutels of versiechecks zodat herhalingen geen nieuwe rijen aanmaken.
Bewaar expliciete lokale statussen zoals queued, uploading, uploaded en failed zodat de worker kan hervatten zonder te raden. Markeer een item pas als voltooid nadat de server bevestigt en sla genoeg metadata op (zoals file URI en pogingsteller) om na een crash of reboot door te gaan.
Begin met minimale constraints die gebruikers beschermen maar synchronisatie nog steeds de meeste dagen laten draaien: netwerkverbinding vereist, niet uitvoeren bij lage batterij en niet uitvoeren bij kritiek lage opslag. Wees voorzichtig met vereisten als “onbeperkt netwerk” en “opladen” omdat die synchronisatie in het veld vaak laten vastlopen.
Behandel “verbonden maar geen internet” als een normale fout: time-out snel, return Result.retry() en probeer later opnieuw. Als je het tijdens het verzoek kunt detecteren, toon dan een simpele melding zodat de gebruiker begrijpt waarom het apparaat online lijkt maar synchronisatie niet doorgaat.
Gebruik exponentiële backoff voor netwerkfouten zodat retries minder frequent worden bij slechte dekking. Retry time-outs en 5xx-errors, fail snel bij permanente problemen zoals ongeldige verzoeken, en cap het aantal pogingen zodat je niet eeuwig blijft loopen wanneer de gebruiker moet ingrijpen (bijv. opnieuw inloggen).
Enqueue synchronisatie als unieke work zodat meerdere triggers geen parallelle jobs starten, en toon voortgang die gebruikers vertrouwen voor lange uploads. Als het werk langloopt of door de gebruiker is gestart, run het als foreground work met een lopende notificatie die echte aantallen toont en een duidelijke annuleeroptie biedt.


