20 apr 2025·8 min leestijd

Kotlin-netwerken voor trage verbindingen: timeouts en veilige retries

Praktische Kotlin-netwerken voor trage verbindingen: stel timeouts in, cache verstandig, retry zonder duplicaten en bescherm kritieke acties op wankele mobiele netwerken.

Kotlin-netwerken voor trage verbindingen: timeouts en veilige retries

Wat breekt er bij trage en onbetrouwbare verbindingen

Op mobiel betekent “traag” meestal niet “geen internet”. Het betekent vaak een verbinding die werkt, maar alleen in korte bursts. Een verzoek kan 8 tot 20 seconden duren, halverwege vastlopen en daarna toch nog slagen. Of het werkt het ene moment en faalt het het volgende omdat de telefoon van Wi‑Fi naar LTE schakelde, in een gebied met slecht signaal kwam, of het OS de app naar de achtergrond verplaatste.

“Onbetrouwbaar” is erger. Packets vallen weg, DNS-queries timen uit, TLS-handshakes mislukken en verbindingen worden willekeurig gereset. Je kunt alles “goed” in code doen en toch fouten zien in het veld omdat het netwerk onder je verandert.

Daar breken default instellingen vaak. Veel apps vertrouwen op bibliotheek-standaarden voor timeouts, retries en caching zonder te bepalen wat “goed genoeg” is voor echte gebruikers. Defaults zijn vaak afgesteld op stabiel Wi‑Fi en snelle APIs, niet op een forensentrein, een lift of een druk café.

Gebruikers beschrijven geen “socket timeouts” of “HTTP 503”. Zij merken symptomen: eindeloze spinners, plotselinge fouten na lang wachten (maar het werkt bij de volgende poging), dubbele acties (twee boekingen, twee bestellingen, dubbele afschrijvingen), verloren updates en mixed states waarbij de UI zegt “mislukt” terwijl de server wel succesvol was.

Trage netwerken vergroten kleine ontwerpgaten tot problemen met geld en vertrouwen. Als de app niet duidelijk onderscheidt tussen “bezig met verzenden”, “mislukt” en “klaar”, tikken gebruikers opnieuw. Als de client blind herhaalt, kunnen duplicaten ontstaan. Als de server geen idempotentie ondersteunt, kan één wankele verbinding meerdere “succesvolle” writes opleveren.

“Kritieke acties” zijn alles wat hooguit één keer moet gebeuren en correct moet zijn: betalingen, checkout-submits, een tijdslot boeken, punten overdragen, wachtwoord wijzigen, een verzendadres opslaan, een claim indienen of een goedkeuring versturen.

Een realistisch voorbeeld: iemand verstuurt de checkout op zwak LTE. De app stuurt de request, maar de verbinding valt weg voordat de response binnen is. De gebruiker ziet een fout en tikt nogmaals op “Betalen”, en nu bereiken twee requests de server. Zonder heldere regels kan de app niet bepalen of hij moet retryen, wachten of stoppen. De gebruiker weet niet of hij het opnieuw moet proberen.

Bepaal je regels voordat je code afstelt

Wanneer verbindingen traag of onbetrouwbaar zijn, komen de meeste bugs voort uit onduidelijke regels, niet uit de HTTP-client. Voordat je timeouts, caching of retries aanpakt, schrijf op wat “correct” betekent voor jouw app.

Begin met acties die nooit twee keer mogen draaien. Dat zijn meestal geld- en accountacties: bestelling plaatsen, kaart belasten, uitbetaling aanvragen, wachtwoord veranderen, account verwijderen. Als een gebruiker twee keer tikt of de app retryt, moet de server het toch als één request behandelen. Als je dat nog niet kunt garanderen, behandel die endpoints als “geen automatische retry” totdat het veilig is.

Bepaal vervolgens wat elk scherm mag doen als het netwerk slecht is. Sommige schermen kunnen offline nuttig blijven (laatst bekende profiel, vorige bestellingen). Andere moeten read-only worden of een duidelijke “probeer opnieuw”-staat tonen (voorraad, live prijzen). Het mixen van verwachtingen leidt tot verwarrende UI en riskante caching.

Stel acceptabele wachttijden per actie vast op basis van hoe gebruikers denken, niet wat netjes voelt in code. Inloggen kan een korte wachttijd verdragen. Bestanden uploaden vraagt meer tijd. Checkout moet snel aanvoelen maar ook veilig zijn. Een timeout van 30 seconden kan op papier “betrouwbaar” lijken en toch gebroken voelen.

Bepaal tenslotte wat je op het apparaat opslaat en hoe lang. Caching helpt, maar verouderde data kan tot verkeerde keuzes leiden (oude prijzen, verlopen rechten).

Schrijf de regels ergens neer waar iedereen ze kan vinden (een README is prima). Houd het simpel:

  • Welke endpoints mogen niet dupliceren en vereisen idempotentie-handling?
  • Welke schermen moeten offline werken en welke zijn read-only offline?
  • Wat is de maximale wachttijd per actie (inloggen, feed-refresh, upload, checkout)?
  • Wat kan op het apparaat gecachet worden en wat is de vervaldatum?
  • Na een mislukking: laat je een fout zien, queue je voor later of vereist het handmatige retry?

Zodra deze regels duidelijk zijn, worden je timeoutwaarden, caching-headers, retrybeleid en UI-staten veel eenvoudiger te implementeren en te testen.

Timeouts die overeenkomen met echte gebruikersverwachting

Trage netwerken falen op verschillende manieren. Een goede timeout-configuratie kiest niet zomaar een getal; het past bij wat de gebruiker probeert te doen en faalt snel genoeg zodat de app kan herstellen.

De drie timeouts, in eenvoudige termen:

  • Connect timeout: hoe lang je wacht om een verbinding met de server tot stand te brengen (DNS-lookup, TCP, TLS). Als dit faalt, is het verzoek nooit echt begonnen.
  • Write timeout: hoe lang je wacht tijdens het versturen van de request-body (uploads, grote JSON, trage uplink).
  • Read timeout: hoe lang je wacht tot de server data terugstuurt nadat de request verzonden is. Dit toont zich vaak bij wankele mobiele netwerken.

Timeouts moeten het scherm en de inzet weerspiegelen. Een feed mag langzamer laden zonder groot probleem. Een kritieke actie moet óf duidelijk slagen óf duidelijk falen zodat de gebruiker weet wat te doen.

Een praktische startpunt (pas aan na meten):

  • Lijst laden (laag risico): connect 5–10s, read 20–30s, write 10–15s.
  • Search-as-you-type: connect 3–5s, read 5–10s, write 5–10s.
  • Kritieke acties (hoog risico, zoals “Betalen” of “Bestelling verzenden”): connect 5–10s, read 30–60s, write 15–30s.

Consistentie is belangrijker dan perfectie. Als de gebruiker op “Verzenden” tikt en twee minuten een spinner ziet, tikt hij opnieuw.

Voorkom “oneindig laden” door ook in de UI een duidelijke bovengrens te tonen. Laat direct voortgang zien, bied annuleren aan en toon na (zeg) 20–30 seconden “Nog steeds bezig…” met opties om opnieuw te proberen of de verbinding te controleren. Dat houdt de ervaring eerlijk, zelfs als de netwerklibrary nog wacht.

Als een timeout optreedt, log dan genoeg om patronen later te debuggen, zonder geheimen vast te leggen. Handige velden zijn het URL-pad (niet de volledige query), HTTP-methode, status (indien aanwezig), een tijdsopsplitsing (connect vs write vs read als beschikbaar), netwerktype (Wi‑Fi, mobiel, vliegtuigmodus), geschatte request/response-grootte en een request-ID zodat je client-logs met server-logs kunt koppelen.

Een eenvoudige, consistente Kotlin network-setup

Als verbindingen traag zijn, veranderen kleine inconsistenties in client-setup in grote problemen. Een schoon baseline helpt sneller debuggen en geeft elk verzoek dezelfde regels.

Eén client, één beleid

Begin met één plek waar je je HTTP-client bouwt (vaak één OkHttpClient gebruikt door Retrofit). Zet daar de basisregels zodat elk verzoek hetzelfde gedrag heeft: standaard headers (app-versie, locale, auth token) en een duidelijke User-Agent, timeouts één keer instellen (niet verspreid over calls), logging die je kunt inschakelen voor debug en één retry-beslissing (zelfs als dat “geen automatische retries” is).

Hier is een klein voorbeeld dat configuratie op één plek houdt:

val okHttp = OkHttpClient.Builder()
  .connectTimeout(10, TimeUnit.SECONDS)
  .readTimeout(20, TimeUnit.SECONDS)
  .writeTimeout(20, TimeUnit.SECONDS)
  .callTimeout(30, TimeUnit.SECONDS)
  .addInterceptor { chain ->
    val request = chain.request().newBuilder()
      .header("User-Agent", "MyApp/${BuildConfig.VERSION_NAME}")
      .header("Accept", "application/json")
      .build()
    chain.proceed(request)
  }
  .build()

val retrofit = Retrofit.Builder()
  .baseUrl(BASE_URL)
  .client(okHttp)
  .addConverterFactory(MoshiConverterFactory.create())
  .build()

Centrale foutafhandeling die naar gebruikersberichten mappt

Netwerkfouten zijn niet zomaar “een exceptie”. Als elk scherm ze anders behandelt, krijgen gebruikers willekeurige berichten.

Maak één mapper die falen omzet naar een klein aantal gebruikersvriendelijke uitkomsten: geen verbinding/vliegtuigmodus, timeout, serverfout (5xx), validatie- of authfout (4xx) en een onbekende fallback.

Dat houdt de UI-copy consistent (“Geen verbinding” vs “Probeer opnieuw”) zonder technische details te tonen.

Tag en annuleer requests als schermen sluiten

Op wankele netwerken kunnen calls laat klaar zijn en een scherm updaten dat al verdwenen is. Maak annuleren een standaardregel: als een scherm sluit, stopt het werk.

Met Retrofit en Kotlin-coroutines annuleert het cancelen van de coroutine-scope (bijvoorbeeld in een ViewModel) de onderliggende HTTP-call. Voor niet-coroutine calls houd je een referentie naar de Call en roep je cancel() aan. Je kunt ook requests taggen en groepen calls annuleren wanneer een feature wordt verlaten.

Achtergrondwerk mag niet van de UI afhangen

Alles wat belangrijk is en afgerond moet worden (een rapport verzenden, een wachtrij synchroniseren, een submit afronden) moet draaien in een scheduler die daarvoor bedoeld is. Op Android is WorkManager de gebruikelijke keuze omdat die later kan retryen en app-restarts overleeft. Houd UI-acties licht en draag langer werk over aan background-jobs wanneer dat logisch is.

Cachingregels die veilig zijn op mobiel

Own the source code
Krijg echte broncode die je kunt exporteren, reviewen en deployen waar nodig.
Genereer code

Caching kan op trage verbindingen veel winst opleveren omdat het herhaalde downloads vermindert en schermen directer laat aanvoelen. Het kan ook een probleem zijn als het verouderde data toont op het verkeerde moment, zoals een oude rekeningstand of een verlopen bezorgadres.

Een veilige aanpak is alleen cachen wat de gebruiker acceptabel iets ouder kan zien, en dwing een verse check af voor alles wat geld, security of een definitieve beslissing beïnvloedt.

Cache-Control basics waarop je kunt vertrouwen

De meeste regels komen neer op een paar headers:

  • max-age=60: je kunt de gecachte respons 60 seconden hergebruiken zonder de server te vragen.
  • no-store: sla deze respons helemaal niet op (beste voor tokens en gevoelige schermen).
  • must-revalidate: als het is verlopen moet je eerst bij de server checken voordat je het weer gebruikt.

Op mobiel voorkomt must-revalidate “stil verkeerde” data na een tijdelijke offline periode. Als de gebruiker de app opent na een metrorit, wil je een snel scherm, maar je wilt ook dat de app bevestigt wat nog klopt.

ETag-refreshes: snel, goedkoop en betrouwbaar

Voor read-endpoints is ETag-validatie vaak beter dan lange max-age waarden. De server stuurt een ETag mee. De volgende keer stuurt de app If-None-Match met die waarde. Als er niets veranderd is, antwoordt de server 304 Not Modified, wat klein en snel is op zwakke netwerken.

Dit werkt goed voor productlijsten, profieldetails en instellingen.

Een eenvoudige vuistregel:

  • Cache “read” endpoints met korte max-age plus must-revalidate, en ondersteun ETag waar mogelijk.
  • Cache geen “write” endpoints (POST/PUT/PATCH/DELETE). Behandel ze als altijd netwerkgebonden.
  • Gebruik no-store voor alles wat gevoelig is (auth-responses, betaalstappen, privéberichten).
  • Cache statische assets (iconen, publieke config) langer, omdat het risico op veroudering klein is.

Houd caching-beslissingen consistent door de app. Gebruikers merken inconsistenties eerder op dan kleine vertragingen.

Veilige retries zonder het erger te maken

Turn rules into APIs
Ontwerp datamodellen en API-endpoints visueel, en lever consistente gedrag over alle clients.
Maak API

Retries lijken een eenvoudige oplossing, maar ze kunnen averechts werken. Retry de verkeerde requests en je creëert extra load, verbruikt batterij en laat de app hangen.

Begin met alleen retrys bij fouten die waarschijnlijk tijdelijk zijn. Een weggevallen verbinding, een read-timeout of een korte serverstoring kan bij de volgende poging slagen. Een fout wachtwoord, een ontbrekend veld of een 404 niet.

Een praktisch regelschema:

  • Retry timeouts en connect-fouten.
  • Retry 502, 503 en soms 504.
  • Retry niet 4xx (behalve 408 of 429 als je een duidelijke wachttijdregel hebt).
  • Retry geen requests die al bij de server zijn aangekomen en mogelijk verwerkt worden.
  • Houd retries laag (vaak 1 tot 3 pogingen).

Backoff + jitter: minder retry-stormen

Als veel gebruikers dezelfde storing treffen, kunnen directe retries een golf van verkeer veroorzaken die het herstel vertraagt. Gebruik exponentiële backoff (telkens langer wachten) en voeg jitter toe (een kleine willekeurige vertraging) zodat apparaten niet synchroon retryen.

Bijvoorbeeld: wacht ~0,5 seconden, dan 1 seconde, dan 2 seconden, met een random +/- 20%.

Zet een limiet op totale retry-tijd

Zonder limieten kunnen retries gebruikers in een spinner vangen voor minuten. Kies een maximale totale tijd voor de hele operatie, inclusief alle wachttijden. Veel apps mikken op 10–20 seconden voordat ze stoppen en een duidelijke optie tonen om opnieuw te proberen.

Stem ook af op de context. Als iemand een formulier indient wil die snel een antwoord. Als een achtergrondsync faalt, kun je later retryen.

Retry nooit automatisch niet-idempotente acties (zoals een bestelling plaatsen of een betaling uitvoeren) tenzij je bescherming hebt zoals een idempotency key of server-side duplicate check. Als je die veiligheid niet kunt garanderen, faal dan duidelijk en laat de gebruiker beslissen wat te doen.

Dubbele preventie voor kritieke acties

Op een trage of onbetrouwbare verbinding tikken gebruikers vaak twee keer. Het OS kan in de achtergrond opnieuw proberen. Je app kan na een timeout opnieuw sturen. Als de actie iets “maakt” (bestelling, geld overmaken, wachtwoord veranderen), kunnen duplicaten pijn doen.

Idempotentie betekent dat dezelfde request hetzelfde resultaat moet opleveren. Als het verzoek wordt herhaald, moet de server hetzelfde resultaat teruggeven of “al gedaan” melden.

Gebruik een idempotency key voor elke kritieke poging

Voor kritieke acties genereer je een unieke idempotency key zodra de gebruiker begint met de poging, en stuur die mee met de request (vaak als header zoals Idempotency-Key, of als veld in de body).

Een praktisch proces:

  • Maak een UUID idempotency key wanneer de gebruiker op “Betalen” tikt.
  • Sla deze lokaal op met een klein record: status = pending, createdAt, hash van de payload.
  • Stuur de request met de key.
  • Bij succesvolle response: status = done en sla het server result ID op.
  • Als je moet retryen, hergebruik dan dezelfde key, niet een nieuwe.

Die regel “hergebruik dezelfde key” is wat dubbele afschrijvingen voorkomt.

Omgaan met app-restarts en offline gaps

Als de app midden in een request wordt gedood, moet de volgende start alsnog veilig zijn. Sla de idempotency key en request-state lokaal op (bijv. een kleine database‑rij). Bij herstart of reconnect kun je opnieuw proberen met dezelfde key of een “check status” endpoint aanroepen met de opgeslagen key of server result ID.

Server-side moet het contract duidelijk zijn: bij een duplicate key moet de tweede poging worden afgewezen of moet de originele respons worden teruggegeven (zelfde order ID,zelfde receipt). Als de server dat nog niet kan, zal client-side duplicaatpreventie nooit volledig betrouwbaar zijn, omdat de app niet kan zien wat er gebeurde nadat het verzoek al verzonden was.

Een goede gebruikersgerichte toevoeging: als een poging pending is, toon “Betaling bezig” en schakel de knop uit tot je een definitief resultaat hebt.

UI‑patronen die onbedoelde herhaalde submits verminderen

Build for flaky networks
Bouw een mobiele app en backend met duidelijke retry- en timeoutregels vanaf dag één.
Probeer AppMaster

Trage verbindingen veranderen niet alleen requests; ze veranderen hoe mensen tikken. Als het scherm twee seconden vastloopt, gaan veel gebruikers ervan uit dat er niets gebeurde en tikken ze nog eens. Je UI moet “één tik” betrouwbaar laten voelen, zelfs als het netwerk dat niet is.

Optimistische UI is veilig wanneer de actie omkeerbaar of laag risico is, zoals een item favoriet maken, een concept opslaan of een bericht als gelezen markeren. Bevestigde UI is beter voor geld, voorraad, onherroepelijke deletes en alles dat duplicaten kan creëren.

Een goed default voor kritieke acties is een duidelijke pendingstaat. Na de eerste tik schakel je direct de primaire knop om naar een “Submitting…”-staat, deactiveer je hem en toon je een korte regel die uitlegt wat er gebeurt.

Patronen die goed werken op wankele netwerken:

  • Schakel de primaire actie uit na een tap en houd hem uitgeschakeld tot je een definitief resultaat hebt.
  • Toon een zichtbare “Pending”-status met details (bedrag, ontvanger, aantal items).
  • Voeg een eenvoudige “Recente activiteit” view toe zodat gebruikers kunnen bevestigen wat ze al verzonden hebben.
  • Houd de pendingstaat vast als de app naar de achtergrond gaat en keert terug.
  • Geef de voorkeur aan één duidelijke primaire knop boven meerdere tikbare targets op hetzelfde scherm.

Soms slaagt het verzoek maar gaat de response verloren. Beschouw dit als een normale uitkomst, geen fout die uitnodigt tot nieuwe taps. In plaats van “Mislukt, probeer opnieuw”, toon je “We weten het nog niet” en bied je een veilige volgende stap zoals “Controleer status”. Als je de status niet kunt checken, bewaar dan het pending-record lokaal en vertel de gebruiker dat je update zodra de verbinding terug is.

Maak “Probeer opnieuw” expliciet en veilig. Laat het alleen zien wanneer je de request kunt herhalen met hetzelfde client-side request ID of idempotency key.

Realistisch voorbeeld: een onbetrouwbare checkout-submissie

Ship native apps faster
Maak native Android- en iOS-apps die trage verbindingen en consistente staten goed afhandelen.
Bouw mobiele app

Een klant zit in de trein met wisselend signaal. Ze voegen items toe aan de winkelwagen en tikken op Betalen. De app moet geduldig zijn, maar mag ook geen twee bestellingen creëren.

Een veilige volgorde ziet er zo uit:

  1. De app maakt een client-side attempt ID en stuurt de checkout-request met een idempotency key (bijv. een UUID opgeslagen met de cart).
  2. De request wacht op een duidelijke connect-timeout en daarna een langere read-timeout. De trein rijdt een tunnel in en de call timet out.
  3. De app probeert één keer opnieuw, maar alleen na een korte vertraging en alleen als hij nooit een server-response heeft ontvangen.
  4. De server ontvangt de tweede request en ziet dezelfde idempotency key, dus hij geeft het originele resultaat terug in plaats van een nieuwe bestelling aan te maken.
  5. De app toont een definitief bevestigingsscherm wanneer de success-response binnenkomt, ook als die van de retry komt.

Caching volgt strikte regels. Productlijsten, bezorgopties en belastingtabellen mogen kort gecachet worden (GET-requests). De checkout-submissie (POST) wordt nooit gecachet. Zelfs als je een HTTP-cache gebruikt, behandel die als read-only hulp voor browsen, niet als iets dat een betaling kan “onthouden”.

Duplicaatpreventie is een mix van netwerk- en UI-keuzes. Wanneer de gebruiker op Betalen tikt, wordt de knop uitgeschakeld en toont het scherm “Bestelling wordt verzonden...” met één Cancel-optie. Als de app netwerk verliest, schakelt hij naar “Nog steeds bezig” en behoudt dezelfde attempt ID. Als de gebruiker de app geforceerd sluit en opnieuw opent, kan de app hervatten door de orderstatus te controleren met die ID, in plaats van opnieuw te vragen om te betalen.

Snelle checklist en volgende stappen

Als je app “vooral prima” lijkt op kantoor Wi‑Fi maar uit elkaar valt in treinen, liften of landelijke gebieden, beschouw dit als een release-gate. Dit werk gaat minder over slimme code en meer over duidelijke regels die je kunt herhalen.

Checklist voordat je uitrolt:

  • Stel timeouts in per endpoint-type (login, feed, upload, checkout) en test op gedempte en hoge-latency netwerken.
  • Retry alleen waar het echt veilig is en begrens met backoff (een paar pogingen voor reads, meestal geen voor writes).
  • Voeg een idempotency key toe voor elke kritieke write (betalingen, bestellingen, formulier-submits) zodat een retry of dubbele tap geen duplicaten maakt.
  • Maak cachingregels expliciet: wat mag verouderd zijn, wat moet vers zijn en wat nooit gecachet mag worden.
  • Maak staten zichtbaar: pending, failed en completed moeten er verschillend uitzien, en de app moet voltooide acties onthouden na een restart.

Als één van deze punten “beslissen we later” is, eindig je met willekeurig gedrag over schermen heen.

Volgende stappen om het vast te houden

Schrijf een eendelige netwerkpolicy: endpointcategorieën, target-timeouts, retryregels en cachingverwachtingen. Handhaaf het op één plek (interceptors, een gedeelde clientfactory of een klein wrapperlaagje) zodat elk teamlid standaard hetzelfde gedrag krijgt.

Doe daarna een korte duplicaat-drill. Kies één kritieke actie (zoals checkout), simuleer een vastgelopen spinner, force-close de app, zet vliegtuigmodus aan en tik opnieuw op de knop. Als je het niet veilig kunt aantonen, zullen gebruikers het uiteindelijk breken.

Als je deze regels zowel op backend als clients consistent wilt doorvoeren zonder alles handmatig te koppelen, kan AppMaster (appmaster.io) helpen door productieklare backend- en native mobiele broncode te genereren. Zelfs dan is de sleutel de policy: definieer idempotentie, retries, caching en UI-staten één keer en pas ze consequent toe over de hele flow.

FAQ

What’s the first thing I should do before tweaking timeouts and retries?

Begin met te definiëren wat “correct” betekent voor elk scherm en elke actie, vooral alles dat maximaal één keer mag gebeuren, zoals betalingen of bestellingen. Zodra de regels duidelijk zijn, stel je timeouts, retries, caching en UI-staten af op die regels in plaats van te vertrouwen op bibliotheekdefaults.

What are the most common symptoms users notice on slow or flaky networks?

Gebruikers zien meestal eindeloze laadwielen, fouten na een lange wachttijd, acties die pas bij een tweede poging werken of dubbele resultaten zoals twee bestellingen of dubbele afschrijvingen. Dit komt vaak door onduidelijke retry- en “pending vs failed”-regels, niet alleen door zwak signaal.

How should I think about connect, read, and write timeouts on mobile?

Gebruik connect-timeout voor hoe lang je wacht om een verbinding tot stand te brengen, write-timeout voor het versturen van de request-body (uploads) en read-timeout voor het wachten op de respons nadat de request is verzonden. Een redelijke vuistregel is kortere timeouts voor laag-risico leesoperaties en langere read/write-timeouts voor kritieke submissions, met een duidelijke UI-limiet zodat gebruikers niet eindeloos hoeven te wachten.

If I can only set one timeout in OkHttp, which one should it be?

Ja — als je er maar één kunt instellen, gebruik dan callTimeout om de hele operatie end-to-end te begrenzen zodat je “oneindig” wachten voorkomt. Leg daarbovenop connect/read/write-timeouts als je meer controle nodig hebt, vooral voor uploads en langzame responses.

Which errors are usually safe to retry, and which aren’t?

Begin met het opnieuw proberen van tijdelijke fouten zoals verbroken verbindingen, DNS-problemen en timeouts, en soms 502/503/504. Vermijd het automatisch herhalen van 4xx-fouten en herhaal geen writes tenzij je idempotentie bescherming hebt, want retries kunnen duplicaten veroorzaken.

How do I add retries without making the app feel stuck?

Gebruik een klein aantal retries (vaak 1–3) met exponentiële backoff en wat willekeurige jitter zodat veel apparaten niet tegelijk opnieuw proberen. Beperk ook de totale tijd voor retries zodat de gebruiker een duidelijk resultaat krijgt in plaats van een spinner van minutenlang.

What is idempotency, and why does it matter for payments and orders?

Idempotentie betekent dat het herhalen van dezelfde request geen nieuw resultaat oplevert, dus een dubbele tap of retry leidt niet tot dubbele kosten of dubbele boeking. Voor kritieke acties stuur je per poging een idempotency key mee en hergebruik je die bij retries zodat de server het originele resultaat kan teruggeven in plaats van een nieuw record te maken.

How should I generate and store an idempotency key on Android?

Genereer een unieke sleutel wanneer de gebruiker de actie start, sla deze lokaal op met een klein “pending” record en stuur hem mee met de request. Als je retryt of de app herstart, hergebruik dan dezelfde sleutel of check de status zodat je nooit één gebruikersintentie in twee server-writes verandert.

What caching rules are safest for mobile apps on unreliable connections?

Cache alleen data die veilig wat ouder mag zijn, en forceer frisse checks voor geld, security en definitieve beslissingen. Voor reads geef de voorkeur aan korte freshness met revalidatie en overweeg ETags; voor writes cache je niets, en gebruik no-store voor gevoelige responses.

What UI patterns reduce double taps and accidental resubmits on slow networks?

Schakel de primaire knop uit na de eerste tap, toon direct een “Submitting…”-toestand en houd een zichtbare pending status aan die surviveert bij backgrounding of restarts. Als de response mogelijk verloren is, zet gebruikers dan niet aan tot herhaalde taps; toon in plaats daarvan onzekerheid (“We’re not sure yet”) en bied een veilige volgende stap zoals het controleren van de status.

Gemakkelijk te starten
Maak iets geweldigs

Experimenteer met AppMaster met gratis abonnement.
Als je er klaar voor bent, kun je het juiste abonnement kiezen.

Aan de slag