13 apr 2025·6 min leestijd

Kotlin Coroutines vs RxJava voor netwerken en achtergrondwerk

Kotlin Coroutines vs RxJava: vergelijk annulering, foutafhandeling en testpatronen voor netwerk- en achtergrondwerk in echte Android-apps.

Kotlin Coroutines vs RxJava voor netwerken en achtergrondwerk

Waarom deze keuze belangrijk is voor productie-netwerken

Netwerken en achtergrondwerk in een echte Android-app is meer dan één API-aanroep. Het omvat inloggen en token-refresh, schermen die tijdens het laden gedraaid worden, synchronisatie nadat de gebruiker een scherm verlaat, foto-uploads en periodiek werk dat de batterij niet mag leegtrekken.

De bugs die het meest pijn doen zijn meestal geen syntaxisfouten. Ze verschijnen wanneer async werk langer bestaat dan de UI (leaks), wanneer annulering de UI stopt maar niet de echte request (verspild verkeer en vastzittende loaders), wanneer retries requests vermenigvuldigen (rate limits, bans), of wanneer verschillende lagen fouten anders afhandelen zodat niemand kan voorspellen wat de gebruiker zal zien.

Een beslissing tussen Kotlin Coroutines en RxJava beïnvloedt dagelijkse betrouwbaarheid:

  • Hoe je werk modelleert (one-shot calls versus streams)
  • Hoe annulering doorgegeven wordt
  • Hoe fouten worden gerepresenteerd en aan de UI getoond
  • Hoe je threads voor netwerk, disk en UI controleert
  • Hoe testbaar timing, retries en randgevallen zijn

De patronen hieronder focussen op wat meestal breekt onder load of op trage netwerken: annulering, foutafhandeling, retries en timeouts, en testgewoonten die regressies voorkomen. Voorbeelden blijven kort en praktisch.

Kernmodellen: suspend-calls, streams en Flow

Het belangrijkste verschil tussen Kotlin Coroutines en RxJava is de vorm van het werk dat je modelleert.

Een suspend-functie vertegenwoordigt een one-shot operatie. Ze retourneert één waarde of gooit één fout. Dat past bij de meeste netwerkcalls: profiel ophalen, instellingen bijwerken, foto uploaden. De aanroepende code leest van boven naar beneden, wat gemakkelijk te scannen blijft zelfs nadat je logging, caching en branches toevoegt.

RxJava begint met de vraag of je met één waarde of vele waarden over tijd te maken hebt. Een Single is een one-shot resultaat (success of error). Een Observable (of Flowable) is een stream die meerdere waarden kan emitten, dan complete, of faalt. Dit past bij features die echt event-achtig zijn: tekstveranderingen, websocket-berichten of polling.

Flow is de coroutine-vriendelijke manier om een stream te representeren. Zie het als de "stream-versie" van coroutines, met gestructureerde annulering en een directe match met suspending APIs.

Een snelle vuistregel:

  • Gebruik suspend voor één request en één response.
  • Gebruik Flow voor waarden die in de tijd veranderen.
  • Gebruik RxJava wanneer je app al zwaar leunt op operators en complexe streamcompositie.

Naarmate features groeien, breekt leesbaarheid meestal eerst wanneer je een streammodel forceert op een one-shot call, of wanneer je doorlopende events probeert te behandelen als één returnwaarde. Kies eerst de abstractie die bij de realiteit past, bouw daarna conventies.

Annulering in de praktijk (met korte codevoorbeelden)

Annulering is waar async code of veilig aanvoelt, of verandert in willekeurige crashes en verspilde calls. Het doel is eenvoudig: wanneer de gebruiker een scherm verlaat, moet elk werk gestart voor dat scherm stoppen.

Bij Kotlin Coroutines is annulering ingebouwd in het model. Een Job vertegenwoordigt werk, en met gestructureerde concurrency geef je meestal geen jobs door. Je start werk binnen een scope (zoals een ViewModel-scope). Wanneer die scope geannuleerd wordt, wordt alles binnenin ook geannuleerd.

class ProfileViewModel(
    private val api: Api
) : ViewModel() {

    fun loadProfile() = viewModelScope.launch {
        // If the ViewModel is cleared, this coroutine is cancelled,
        // and so is the in-flight network call (if the client supports it).
        val profile = api.getProfile() // suspend
        // update UI state here
    }
}

Twee productiedetails zijn belangrijk:

  • Roep suspend-netwerkoperaties aan via een annuleerbare client. Anders stopt de coroutine maar kan de HTTP-call blijven lopen.
  • Gebruik withTimeout (of withTimeoutOrNull) voor requests die niet mogen hangen.

RxJava gebruikt expliciete disposal. Je houdt een Disposable voor elke subscription, of verzamelt ze in een CompositeDisposable. Wanneer het scherm weggaat, dispose je, en de keten zou moeten stoppen.

class ProfilePresenter(private val api: ApiRx) {
    private val bag = CompositeDisposable()

    fun attach() {
        bag += api.getProfile()
            .subscribe(
                { profile -> /* render */ },
                { error -> /* show error */ }
            )
    }

    fun detach() {
        bag.clear() // cancels in-flight work if upstream supports cancellation
    }
}

Een praktische regel bij het verlaten van een scherm: als je niet precies kunt aanwijzen waar annulering gebeurt (scope-cancellation of dispose()), ga er dan vanuit dat het werk blijft lopen en los het op voordat je het publiceert.

Foutafhandeling die begrijpelijk blijft

Een groot verschil tussen Kotlin Coroutines en RxJava is hoe fouten reizen. Coroutines laten failures voelen als normale code: een suspend-call gooit, en de aanroeper beslist wat te doen. Rx duwt failures door de stream, wat krachtig is, maar het is makkelijk problemen te verbergen als je niet oplet.

Gebruik exceptions voor onverwachte fouten (timeouts, 500s, parse-bugs). Modelleer fouten als data wanneer de UI een specifieke respons nodig heeft (verkeerd wachtwoord, "e-mail al in gebruik") en je wilt dat dat deel van je domeinmodel is.

Een eenvoudig coroutine-patroon behoudt de stacktrace en blijft leesbaar:

suspend fun loadProfile(): Profile = try {
    api.getProfile() // may throw
} catch (e: IOException) {
    throw NetworkException("No connection", e)
}

runCatching en Result zijn handig wanneer je echt succes of falen wilt teruggeven zonder te gooien:

suspend fun loadProfileResult(): Result<Profile> =
    runCatching { api.getProfile() }

Wees voorzichtig met getOrNull() als je de fout niet ook afhandelt. Dat kan echte bugs stilletjes veranderen in een "lege staat" scherm.

In RxJava houd je het foutpad expliciet. Gebruik onErrorReturn alleen voor veilige fallbacks. Geef de voorkeur aan onErrorResumeNext wanneer je van bron wilt wisselen (bijvoorbeeld naar gecachte data). Voor retries, houd de regels nauw met retryWhen zodat je niet herhaalt bij een "verkeerd wachtwoord."

Een set gewoonten die voorkomt dat fouten worden weggeslikt:

  • Log of rapporteer een fout één keer, dicht bij waar je context hebt.
  • Bewaar de originele exception als cause wanneer je ze wrappet.
  • Vermijd catch-all fallbacks die elke fout in een defaultwaarde veranderen.
  • Maak user-facing fouten een getypeerd model, geen string.

Threading basics: Dispatchers vs Schedulers

Test async flows eerder
Prototypeer login-, profiel- en sync-flows met visuele logica in plaats van breekbare lijmcode.
Probeer het

Veel async-bugs komen neer op threading: zware taken op de main-thread uitvoeren, of UI aanraken vanaf een achtergrondthread. Kotlin Coroutines en RxJava verschillen vooral in hoe je thread-switches uitdrukt.

Met coroutines start je vaak op de main-thread voor UI-werk, en spring je dan naar een achtergrond-dispatcher voor het zware werk. Veelvoorkomende keuzes zijn:

  • Dispatchers.Main voor UI-updates
  • Dispatchers.IO voor blocking I/O zoals netwerk en schijf
  • Dispatchers.Default voor CPU-werk zoals JSON-parsing, sorteren, encryptie

Een eenvoudig patroon is: haal data, parse buiten main, en render dan.

viewModelScope.launch(Dispatchers.Main) {
    val json = withContext(Dispatchers.IO) { api.fetchProfileJson() }
    val profile = withContext(Dispatchers.Default) { parseProfile(json) }
    _uiState.value = UiState.Content(profile)
}

RxJava drukt "waar werk gebeurt" uit met subscribeOn en "waar resultaten geobserveerd worden" met observeOn. Een veelvoorkomende verrassing is verwachten dat observeOn upstream beïnvloedt. Dat doet het niet. subscribeOn zet de thread voor de bron en operators erboven, en elke observeOn schakelt threads vanaf dat punt.

api.fetchProfileJson()
    .subscribeOn(Schedulers.io())
    .map { json -> parseProfile(json) } // still on io unless you change it
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { profile -> render(profile) },
        { error -> showError(error) }
    )

Een regel die verrassingen voorkomt: houd UI-werk op één plaats. In coroutines wijs of verzamel UI-state toe op Dispatchers.Main. In RxJava zet je één finale observeOn(main) vlak voor renderen en strooi je niet te veel extra observeOn-calls tenzij echt nodig.

Als een scherm stottert, verplaats parsing en mapping eerst van de main-thread. Die ene wijziging lost veel echte problemen op.

Retries, timeouts en parallel werk voor netwerkcalls

Zet patronen om in een app
Bouw een productieklare Android-app met gegenereerde Kotlin-code en duidelijke backend-API's.
Probeer AppMaster

Het happy path is zelden het probleem. Problemen ontstaan door calls die hangen, retries die het erger maken, of "parallel" werk dat eigenlijk niet parallel is. Deze patronen bepalen vaak of een team voorkeur heeft voor Kotlin Coroutines of RxJava.

Timeouts die snel falen

Met coroutines kun je een harde limiet rond elke suspend-call zetten. Houd de timeout dicht bij de aanroep zodat je de juiste UI-boodschap kunt tonen.

val user = withTimeout(5_000) {
    api.getUser() // suspend
}

In RxJava koppel je een timeout-operator aan de stream. Dat is handig wanneer timeout-gedrag onderdeel moet zijn van een gedeelde pipeline.

Retries zonder schade

Retry alleen wanneer retry veilig is. Een simpele regel: retry idempotente requests (zoals GET) vrijer dan requests die side-effects creëren (zoals "create order"). Beperk het aantal retries en voeg delay of jitter toe.

Goede standaard-guardrails:

  • Retry bij netwerk-timeouts en tijdelijke serverfouten.
  • Retry niet bij validatiefouten (400s) of auth-fouten.
  • Beperk retries (vaak 2–3) en log de uiteindelijke fout.
  • Gebruik backoff delays zodat je de server niet nat maakt.

In RxJava laat retryWhen je uitdrukken "retry alleen voor deze fouten, met deze vertraging." In coroutines heeft Flow retry en retryWhen, terwijl gewone suspend-functies vaak een kleine loop met delays gebruiken.

Parallelle calls zonder verwarde code

Coroutines maken parallel werk direct: start twee requests, wacht op beide.

coroutineScope {
    val profile = async { api.getProfile() }
    val feed = async { api.getFeed() }
    profile.await() to feed.await()
}

RxJava blinkt uit wanneer het combineren van meerdere bronnen het hele punt van de keten is. zip is de gebruikelijke "wacht op beide" tool, en merge is handig wanneer je resultaten wilt zodra ze binnenkomen.

Voor grote of snelle streams blijft backpressure belangrijk. RxJava's Flowable heeft volwassen backpressure-tools. Coroutines Flow dekt veel gevallen goed af, maar je hebt soms buffering- of dropping-policies nodig als events je UI of database-writes kunnen overrompelen.

Interop en migratiepatronen (gemengde codebases)

De meeste teams schakelen niet in één keer om. Een praktische Kotlin Coroutines vs RxJava-migratie houdt de app stabiel terwijl je module-voor-module migreert.

Wrap een Rx-API in een suspend-functie

Als je een bestaande Single<T> of Completable hebt, wrappet je die met annulering-ondersteuning zodat een geannuleerde coroutine de Rx-subscriptie disposeert.

suspend fun <T : Any> Single<T>.awaitCancellable(): T =
  suspendCancellableCoroutine { cont ->
    val d = subscribe(
      { value -> cont.resume(value) {} },
      { error -> cont.resumeWithException(error) }
    )
    cont.invokeOnCancellation { d.dispose() }
  }

Dit voorkomt een veelvoorkomende foutmodus: de gebruiker verlaat het scherm, de coroutine wordt geannuleerd, maar de netwerkcall blijft lopen en werkt later gedeelde staat bij.

Stel coroutine-code beschikbaar aan Rx-callers

Tijdens migratie verwachten sommige lagen nog Rx-typen. Wrappet suspend-werk in Single.fromCallable en block alleen op een achtergrondthread.

fun loadProfileRx(api: Api): Single<Profile> =
  Single.fromCallable {
    runBlocking { api.loadProfile() } // ensure subscribeOn(Schedulers.io())
  }

Houd deze grens klein en goed gedocumenteerd. Voor nieuwe code heeft de voorkeur om de suspend-API direct vanuit een coroutine-scope aan te roepen.

Waar Flow past, en waar niet

Flow kan veel Observable-use-cases vervangen: UI-state, database-updates en paging-achtige streams. Het kan minder direct zijn als je sterk vertrouwt op hot streams, subjects, geavanceerde backpressure-tuning, of een grote set custom operators waar je team al mee werkt.

Een migratiestrategie die verwarring beperkt:

  • Converteer leaf-modules eerst (netwerk, storage) naar suspend-API's.
  • Voeg kleine adapters toe op modulegrenzen (Rx naar suspend, suspend naar Rx).
  • Vervang Rx-streams door Flow alleen wanneer je ook de consumenten controleert.
  • Houd één async-stijl per featuregebied.
  • Verwijder adapters zodra de laatste caller gemigreerd is.

Testpatronen die je echt gebruikt

Voorkom technische schuld vroeg
Krijg echte broncode die je kunt reviewen, uitbreiden en onderhouden als de eisen veranderen.
Genereer code

Timing- en annuleringproblemen zijn waar async-bugs zich verbergen. Goede async-tests maken tijd deterministisch en uitkomsten eenvoudig te verifiëren. Dit is weer een gebied waar Kotlin Coroutines vs RxJava verschillend aanvoelt, hoewel beide goed getest kunnen worden.

Coroutines: runTest, TestDispatcher en het controleren van tijd

Voor coroutine-code heeft de voorkeur runTest met een test-dispatcher zodat je test niet van echte threads of echte delays afhangt. Virtuele tijd laat je timeouts, retries en debounce-vensters triggeren zonder te slapen.

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `emits Loading then Success`() = runTest {
  val dispatcher = StandardTestDispatcher(testScheduler)
  val repo = Repo(api = fakeApi, io = dispatcher)

  val states = mutableListOf<UiState>()
  val job = launch(dispatcher) { repo.loadProfile().toList(states) }

  testScheduler.runCurrent() // run queued work
  assert(states.first() is UiState.Loading)

  testScheduler.advanceTimeBy(1_000) // trigger delay/retry windows
  testScheduler.runCurrent()
  assert(states.last() is UiState.Success)

  job.cancel()
}

Om annulering te testen, annuleer je de collecterende Job (of de parent scope) en controleer je dat je fake API stopt of dat er geen extra states meer worden uitgezonden.

RxJava: TestScheduler, TestObserver, deterministische tijd

Rx-tests combineren meestal een TestScheduler voor tijd en een TestObserver voor asserts.

@Test
fun `disposes on cancel and stops emissions`() {
  val scheduler = TestScheduler()
  val observer = TestObserver<UiState>()

  val d = repo.loadProfileRx(scheduler)
    .subscribeWith(observer)

  scheduler.triggerActions()
  observer.assertValueAt(0) { it is UiState.Loading }

  d.dispose()
  scheduler.advanceTimeBy(1, TimeUnit.SECONDS)
  observer.assertValueCount(1) // no more events after dispose
}

Wanneer je foutpaden test in beide stijlen, focus op de mapping, niet op het exacte exception-type. Assert de UI-state die je verwacht na een 401, een timeout of een malformed response.

Een kleine set checks dekt de meeste regressies:

  • Loading- en eindstaten (Success, Empty, Error)
  • Annuleringscleanup (job geannuleerd, disposable disposed)
  • Foutmapping (HTTP-codes naar gebruikersmeldingen)
  • Geen dubbele emissies na retries
  • Tijd-gebaseerde logica met virtuele tijd, niet echte sleeps

Veelgemaakte fouten die productiebugs veroorzaken

De meeste productieproblemen komen niet door de keuze Kotlin Coroutines versus RxJava. Ze ontstaan door een paar gewoonten die werk langer laten lopen dan je denkt, het dubbel laten draaien of de UI op het verkeerde moment aanraken.

Een veelvoorkomende leak is werk starten in de verkeerde scope. Als je een netwerkcall start vanuit een scope die langer leeft dan het scherm (of je maakt je eigen scope en annuleert die nooit), kan de request afronden nadat de gebruiker vertrokken is en nog steeds proberen state bij te werken. In coroutines ziet dat er vaak uit als standaard een langlevende scope gebruiken. In RxJava is het meestal een vergeten dispose.

Een andere klassieker is "fire and forget." Globale scopes en vergeten Disposables voelen goed totdat het werk zich opstapelt. Een chat-scherm dat bij elk resume ververst kan snel meerdere refresh-jobs krijgen na een paar navigaties, elk geheugen vasthoudend en concurrerend om netwerk.

Retries zijn ook makkelijk fout te doen. Onbeperkte retries, of retries zonder delay, kunnen je backend spammen en de batterij legen. Het is extra gevaarlijk wanneer de fout permanent is, zoals een 401 na logout. Maak retries conditioneel, voeg backoff toe en stop wanneer de fout niet herstelbaar is.

Threading-fouten veroorzaken crashes die moeilijk reproduceerbaar zijn. Je kan JSON op de main-thread parsen of UI updaten vanaf een achtergrondthread, afhankelijk van waar je een dispatcher of scheduler plaatste.

Snelle checks die de meeste issues vangen:

  • Koppel werk aan een lifecycle-owner en annuleer het wanneer die owner eindigt.
  • Maak cleanup duidelijk: annuleer Jobs of clear Disposables op één plek.
  • Zet strikte limieten op retries (aantal, delay en welke fouten in aanmerking komen).
  • Handhaaf één regel voor UI-updates (alleen main-thread) tijdens code reviews.
  • Behandel achtergrondsync als een systeem met constraints, niet als een willekeurige functieaanroep.

Als je Android-apps shipt vanaf gegenereerde Kotlin-code (bijv. van AppMaster), gelden dezelfde valkuilen. Je hebt nog steeds duidelijke conventies nodig voor scopes, annulering, retry-limieten en thread-regels.

Korte checklist voor het kiezen van Coroutines, RxJava of beide

Krijg een werkende basis
Ga van debat over Rx en Coroutines naar een werkbare app-architectuur die je kunt uitrollen.
Begin met bouwen

Begin met de vorm van het werk. De meeste netwerkcalls zijn one-shot, maar apps hebben ook doorlopende signalen zoals connectiviteit, auth-state of live-updates. Het kiezen van de verkeerde abstractie vroeg merk je later aan rommelige annulering en moeilijk leesbare foutpaden.

Een eenvoudige beslisboom (en hoe je het aan je team uitlegt):

  • Eenmalige request (login, profiel ophalen): geef de voorkeur aan een suspend-functie.
  • Doorlopende stream (events, database-updates): geef de voorkeur aan Flow of Rx Observable.
  • UI lifecycle-annulering: coroutines in viewModelScope of lifecycleScope zijn vaak eenvoudiger dan handmatige disposables.
  • Sterke afhankelijkheid van geavanceerde stream-operators en backpressure: RxJava kan nog steeds een betere keuze zijn, vooral in oudere codebases.
  • Complexe retries en foutmapping: kies de aanpak die je team leesbaar kan houden.

Een praktische regel: als één scherm één request doet en één resultaat rendert, houden coroutines de code dicht bij een normale functieaanroep. Als je een pijplijn van veel events bouwt (typen, debounce, vorige requests annuleren, filters combineren), voelt RxJava of Flow vaak natuurlijker.

Consistentie is belangrijker dan perfectie. Twee goede patronen die overal worden gebruikt zijn makkelijker te onderhouden dan vijf "beste" patronen die inconsistent worden toegepast.

Voorbeeldscenario: login, profiel ophalen en achtergrondsync

Maak je backend snel
Modelleer data, voeg authenticatie toe en publiceer endpoints zonder server-boilerplate handmatig te schrijven.
Begin met bouwen

Een veelvoorkomende productieflow is: de gebruiker tikt op Login, je belt een auth-endpoint, haalt daarna het profiel op voor het homescreen en start tenslotte een achtergrondsync. Dit is waar Kotlin Coroutines versus RxJava in het dagelijks onderhoud anders kan aanvoelen.

Coroutines-versie (sequentieel + annuleerbaar)

Met coroutines is de "do dit, dan dat"-vorm natuurlijk. Als de gebruiker het scherm sluit, stopt het annuleren van de scope lopend werk.

suspend fun loginAndLoadProfile(): Result<Profile> = runCatching {
  val token = api.login(email, password) // suspend
  val profile = api.profile("Bearer $token")
  syncManager.startSyncInBackground(token) // fire-and-forget
  profile
}.recoverCatching { e ->
  throw when (e) {
    is HttpException -> when (e.code()) {
      401 -> AuthExpiredException()
      in 500..599 -> ServerDownException()
      else -> e
    }
    is IOException -> NoNetworkException()
    else -> e
  }
}

// UI layer
val job = viewModelScope.launch { loginAndLoadProfile() }
override fun onCleared() { job.cancel() }

RxJava-versie (keten + disposal)

In RxJava is dezelfde flow een keten. Annulering betekent disposal, typisch met een CompositeDisposable.

val d = api.login(email, password)
  .flatMap { token -> api.profile("Bearer $token").map { it to token } }
  .doOnSuccess { (_, token) -> syncManager.startSyncInBackground(token) }
  .onErrorResumeNext { e: Throwable ->
    Single.error(
      when (e) {
        is HttpException -> if (e.code() == 401) AuthExpiredException() else e
        is IOException -> NoNetworkException()
        else -> e
      }
    )
  }
  .subscribe({ (profile, _) -> show(profile) }, { showError(it) })

compositeDisposable.add(d)
override fun onCleared() { compositeDisposable.clear() }

Een minimale testsuite hier moet drie uitkomsten dekken: success, gemapte failures (401, 500s, geen netwerk) en annulering/disposal.

Volgende stappen: kies conventies en houd ze consistent

Teams komen meestal in de problemen omdat patronen per feature verschillen, niet omdat Kotlin Coroutines of RxJava inherent "verkeerd" is. Een korte beslissingsnotitie (zelfs één pagina) bespaart tijd in reviews en maakt gedrag voorspelbaar.

Begin met een duidelijke scheiding: one-shot werk (een enkele netwerkcall die eenmaal terugkeert) versus streams (updates over tijd, zoals websocket-events, locatie of databasewijzigingen). Bepaal de default voor elk en definieer wanneer uitzonderingen toegestaan zijn.

Voeg dan een kleine set gedeelde helpers toe zodat elke feature op dezelfde manier reageert wanneer het netwerk faalt:

  • Één plek om fouten (HTTP-codes, timeouts, offline) naar app-level failures te mappen die je UI begrijpt
  • Standaard timeoutwaarden voor netwerkcalls, met een duidelijke manier om te overschrijven voor lange operaties
  • Een retry-policy die zegt wat veilig te retryen is (bijv. GET vs POST)
  • Een annulering-regel: wat stopt wanneer de gebruiker een scherm verlaat en wat mag doorgaan
  • Logging-regels die support helpen zonder gevoelige data te lekken

Testconventies zijn net zo belangrijk. Spreek af over een standaardaanpak zodat tests niet van echte tijd of echte threads afhangen. Voor coroutines betekent dat meestal een test-dispatcher en gestructureerde scopes. Voor RxJava betekent dat meestal test-schedulers en expliciete disposal. Streef naar snelle, deterministische tests zonder sleeps.

Als je sneller wilt opleveren, is AppMaster (appmaster.io) één optie om backend-API's en Kotlin-gebaseerde mobiele apps te genereren zonder alles zelf te schrijven. Zelfs met gegenereerde code blijven dezelfde productieconventies rond annulering, fouten, retries en testen de sleutel tot voorspelbaar netwerkgedrag.

FAQ

Wanneer moet ik een suspend-functie gebruiken versus een stream voor netwerken?

Default naar suspend voor één request dat eenmaal terugkeert, zoals inloggen of het ophalen van een profiel. Gebruik Flow (of Rx streams) wanneer waarden in de tijd veranderen, zoals websocket-berichten, connectiviteit of database-updates.

Stopt coroutine-annulering daadwerkelijk een lopende netwerkrequest?

Ja, maar alleen als je HTTP-client annuleerbaar is. Coroutines stoppen de coroutine wanneer de scope geannuleerd wordt, maar de onderliggende HTTP-aanvraag moet ook annulering ondersteunen, anders kan de request op de achtergrond blijven lopen.

Wat is de veiligste manier om leaks te voorkomen als de gebruiker een scherm verlaat?

Koppel werk aan een lifecycle-scope, zoals viewModelScope, zodat het geannuleerd wordt wanneer de schermlogica eindigt. Vermijd starten in langlevende of globale scopes tenzij het werk echt app-breed is.

Hoe moet foutafhandeling verschillen tussen Coroutines en RxJava?

In coroutines gooien failures meestal en behandel je ze met try/catch dicht bij waar je ze naar UI-state kunt mappen. In RxJava reizen fouten door de stream, dus houd het foutpad expliciet en vermijd operators die fouten stilletjes in defaultwaarden veranderen.

Moet ik fouten modelleren als exceptions of als data?

Gebruik exceptions voor onverwachte fouten zoals timeouts, 500s of parsefouten. Gebruik getypeerde foutdata wanneer de UI een specifiek antwoord nodig heeft, zoals “verkeerd wachtwoord” of “email al in gebruik”, zodat je niet op stringmatching hoeft te vertrouwen.

Wat is een eenvoudige manier om timeouts toe te voegen zonder de code rommelig te maken?

Pas een timeout toe waar je de juiste UI-tekst kunt tonen, bijvoorbeeld dicht bij de aanroep. In coroutines is withTimeout eenvoudig voor suspend-calls; in RxJava maakt de timeout-operator de timeout onderdeel van de keten.

Hoe implementeer ik retries zonder dubbele requests of bans te veroorzaken?

Retry alleen wanneer het veilig is, meestal voor idempotente requests zoals GET, en beperk het aantal retries tot iets kleins zoals 2–3. Retry niet bij validatiefouten of auth-fouten en voeg vertragingen toe zodat je de server niet overspoelt of de batterij leegtrekt.

Wat is de grootste threading-valkuil met Dispatchers versus Schedulers?

Coroutines gebruiken Dispatchers en starten vaak op Main voor UI, en wisselen dan naar IO of Default voor zwaar werk. RxJava gebruikt subscribeOn voor waar upstream draait en observeOn voor waar je resultaten consumeert; zet één finale switch naar main vlak voor renderen om verrassingen te vermijden.

Kan ik RxJava en Coroutines mixen tijdens een migratie?

Ja, maar houd de grens klein en annulering-bewust. Wrap Rx naar suspend met een annuleerbare adapter die de Rx-subscriptie disposeert bij coroutine-annulering, en bied suspend-werk aan Rx-callers alleen via een kleine, goed gedocumenteerde brug.

Hoe test ik annulering, retries en tijdsgebaseerde logica betrouwbaar?

Gebruik virtuele tijd zodat tests niet slapen of van echte threads afhangen. Voor coroutines gebruik runTest met een test dispatcher zodat je delays en annulering kunt controleren; voor RxJava gebruik TestScheduler en asserteer dat er geen emissies meer zijn na dispose().

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