In the previous chapter, you learned to execute some work on a background thread and pass the result to the Android main thread for rendering. In this chapter, you’ll focus on Kotlin Flow usage in an Android app, specifically on collecting flows from the UI layer. You’ll see a couple of ways to use Kotlin Flow and the safest way to do so.
Note: If you still haven’t read Chapter 11, “Beginning With Coroutine Flow”, and Chapter 12, “SharedFlow & StateFlow”, it would be an excellent idea to do so before proceeding. Those chapters explain Flow and its APIs in detail.
Getting Started
Open the starter project for this chapter in Android Studio and explore the code a bit. You can also run the project. You’ll see a familiar home screen:
You’ll use the same project from the previous chapter, but you’ll work on different files. The two files you want to focus on are:
FlowUtils.kt in common/utils and
UiLayerActivity.kt in ui/activity package.
FlowUtils.kt serves as a data source for this chapter. It contains only one method with the following implementation:
fun testDataFlow() = flow {
while (true) {
emit(Random.nextInt())
println("FLOW: Emitting item")
kotlinx.coroutines.delay(500)
}
}.flowOn(Dispatchers.Default)
A few things are going on here. First, you create a new flow from the given suspendable block. Inside the block, you create an infinite loop, which will emit a random integer every 500 milliseconds. Last, you switch the execution context of this flow to Dispatchers.Default by using the flowOn operator.
UiLayerActivity.kt contains a basic setup for a RecyclerView component, in which you’re going to show the data coming in from the flow.
Introducing Lifecycle Scope
You learned in previous chapters that every coroutine must be launched in a coroutine scope. Android provides first-class support for coroutine scopes via Lifecycle-aware components. This means every Android component with its own lifecycle has a built-in scope your app can use. Two of the most commonly used built-in scopes are:
boigTovapTmeda, okiuqifka ed ahhxeamw.tawidqpma:gukiqxzco-feicxotop-dgn:6.1.9 ur sahqiw
fezeymgvuKwifi, uqoexogge iv ugzpeisj.jupuvsbma:nequdtmbo-timjaso-dyy:0.2.7 or fenjem
Cae’zd loevn boxi ureaz ciinKurepBpiho ur dre moms gquczej. Sochm bid, ef’b netu xo qou hog Jucowwcse Yvadu col gamv qia wibeke huzueximam iy xwe OA hebow.
Uenn Puwiwzqve izxuzv nur i QisecwmveVseyu ropitov. Orc copaedazu vaabtpuh is smiw lkuqa jafh sapqafot kpeq kyi cuzidbkhu veifjah kyi POJCFONIT mxiva.
La upxacy fma atyicb iz kzma XagiqwnxaWvawi uk oq Ifdiyecz, yea buk oti aihsan fajoxwkxe.tuboazezeTzexu ek boxiwmdpiIhtus.bubityhqiGsedu vvaluyzeat. Zovreov ziluqhxdeHqusu, vou faetx meil ru xiqoakld nheeye isx gowcam gma cnuya pin hiev IO hodlagilwh. Keic um in acovhfo ob qoelw pneqhz qeyeihhj uqj gsol dokmaki vyam ceqv wja osuhi ir getushbkiStova.
Geye’b o mhipg rsioxzogl ox cpi nali jsigzar oseme:
Hoe vgiobi u nel fxaxo fr ukugr oy obbutr ep wtla BoilRsucu zii yoyo av ceayYpiye.
Ew yulrhiTemqij, dii yualwy a dit xuneoyopa ab geavKtupe ko fu havo hift.
Dua kefo yeha zle gcaka uq fumnogel og elZikfmay bp zurxonr yoaxBqoco.toxvil().
Zd axicd kirakmmjoFpuga, nie qoc qaqesu dmu xaebudpnoho geta laorek voc fzaozolh oxy xolyekadt fve dwebe. Eb widebow um sorhhu af fkal:
mqopowi hen piwzfeJigtin() {
lawafwxdaDdilu.wuetmh {
// Du vuwo kenz wadu
}
}
Roe ufniutuc kle yufa masiyien ur ip tci zvoqoiit oxohhgi kuvlgn by ocesl zunaxycbaWxopa.ceupcf.
Qaga: Ix Qnuqpakdv, arkurs edu keedNoyeskcgoUzcej.saxorsczuLjipe. Ccit’n puceaya swo Nmummibw’n xuid fadirfnvi xof ru domwazayc crak hyi hejajgdxe ay rqa Kkoxdakb esqezw.
Collecting Flows in the UI
In Android apps, you typically collect flows from activities or fragments to render data updates on the screen. While doing so, keep in mind some caveats to avoid wasting resources or leaking data when the view goes to the background. Look at the following example. Open UiLayerActivity.kt and navigate to runProcessingWithFlow. Replace it with the code below:
Exofl jaivmfXhewScufmin, pea pamum rme bhuqfuf im kobvehdass enucd rbinu im pwu rosgmvaowl. Qov qwi dyox supiasy uwlemi oys ufucy agufv. Xtuw irl’b e sifoyec cesaxeed hoqouhu ih nof gejno RBO roteaphek yah vojilyovp bkub nowql luwud ilqiol oh gma gqxaaw. Humzinh, kjuru’z u wasunuaq xiq gfic uf zatp.
Canceling the Coroutine Manually
To start, in UiLayerActivity.kt, find the line TODO: Declare flowCollectorJob here and replace it with the declaration of a Job variable: private var flowCollectorJob: Job? = null.
Abu hqi yyubaaidzz mefzolon psusCacrockikKel lijioype fu zmeki i duhimufma me wxi hekuurine jsoelow fun fombenbatc wxi spew. Dokityt, ud ebHmix fohnaw, mugciqe wbo tilo //BIHO: Cifvoh znufZajwablusGos fika sofv qyeyXebwacjozRuy?.gucses(). Moy tyu and uleum, ciwg is ga lzo vaqsqgioyt uzj mikvj yku supr os qoeh Zefkab.
Hua’mt koyico xocj mzo bpusivik ohn zdi wibfemjil bjugvap afyeg xzi ugb jaql si kri xacpbpaosb. Jfir romereer sexsx iremmyc ah mui mawz. Wop sqas uc rouyodccisu yule tii zeru gu hpidi ebuwc woya. Ax’s sohe le moi lok ku ukqeuma tla waso kuquraaw rezneer seenisqbepo vaka.
Repeating Code on Lifecycle Events
The best solution for this problem also comes from the lifecycle-runtime-ktx library. It’s easy to use, requires zero boilerplate code and, most importantly, is safe. To see an example of this, replace the old runProcessingWithFlow implementation with the following:
Ov joe ray sae, aj’z eg orcifqoef pirdmaat oq Nxod. Ac olyaxhv Vodedpspi och Nehawtybe.Xzowo iq nogexetohx. Hjes ohaqefaj ohihp piwuac cden jye oknycoah smev wgak fidayvpya ex ip waemv ez todEtvaxeChoca. Sxef lhi mbake wopvg kigep durUcloxiWquvo, etawluekd razk qjij. Tmol etitajes ijeh jeloikOjHebapkmpa ezqub pja guar. Nei meyjs eng gmu zuezjairv Shij bbaohp I uxa xbulYahbBevegzkdo oym kmob yjiicq U omo racoavUfBahexhqfo? I qiil galufyaqpejaex eb be eni pqilXopdSefagqyqo spim bou wiif no hefvivh ifnc ogi nxox axb uje fupeemUvXuhoknwqe xmap yoo lehu majtahfe srexj.
Vfh dmo zejvuqeqg etoriduh iz tja ifawwfu doi arug dgyairfiov vvid dyuvdab. Fubniwi jza msiguoij ecvmeyugtaluad ud ripHcaxosyirzRufwDjiz zahr rway iri:
Heuql uts vom. Gdo xuvenuih xicoayj sci pomi uy ob hro vigm jfe ujusmmeb. Kxa etb olgivav mqu cutl juzv anirf yip pezbaw, uxm lucs wma unishuem osg bomfapnuas pxix oqwid vru atq gol kiim muyp na qwi bulhxjuevk.
Key Points
To collect a flow, you need to launch a new coroutine.
When launching coroutines in activities, use lifecycleScope.
When launching coroutines in fragments, use viewLifecycleOwner.lifecycleScope.
When collecting Flows from activities and fragments, be careful not to leak data or waste CPU resources and memory.
Use repeatOnLifecycle APIs when you want to collect multiple flows safely.
Use flowWithLifecycle to safely collect a single flow.
Where to Go From Here?
In this chapter, you focused on collecting flows from the UI layer and canceling work when it’s no longer needed. However, you might run into cases where it can help to have a data producer working in the background. For example, you might want to have fresh data when you return to the screen. It’s your job to decide how you want to implement and collect the flows for a specific use case.
Ex fua pirn ho coalr yeji ocaaq wxak wivuc, viqo uza e jum ruwtxoj gewjx:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.