You’re getting pretty handy with coroutines, aren’t ya? In the previous chapters of this book you’ve seen how you can start coroutines, bridge between threads in coroutines to return values, create your own APIs and much more. In the next few chapters, you’ll focus on the internal coroutine concepts. And the most important is the CoroutineContext. Even though it’s at the core of every coroutine, you’ll see that it’s fairly simple after you take a look at its implementation and usage.
Contextualizing Coroutines
Each coroutine is tied to a CoroutineContext. The context is a wrapper around a set of CoroutineContext.Elements, each of which describes a vital part that builds up and forms a coroutine, the way exceptions are propagated and execution flow is navigated or just the general lifecycle.
These elements are:
Job: A cancellable piece of work, which has a defined lifecycle.
ContinuationInterceptor: A mechanism which listens to the continuation within a coroutine and intercepts its resumption.
CoroutineExceptionHandler: A construct which handles exceptions in coroutines.
So, when you run launch, you can pass it a context of your own choice. The context defines which elements will fit into the puzzle. If you pass in a Job, which implements CoroutineContext.Element, you’ll define what the new coroutine’s parent is. As such, if the parent job finishes, it will notify all of its children, including the newly created coroutine.
If you pass in an exception handler, another CoroutineContext.Element, you give the coroutine a way to process errors if something bad happens.
And the last thing you can pass in is a ContinuationInterceptor. These constructs control the flow of each coroutine-powered function, by determining which thread it should operate on and how it should distribute work.
You wouldn’t want to write a full implementation that manually handles continuations. If you want something to do that for you, while also being a CoroutineContext.Element, you have to provide a coroutine dispatcher.
You’ve used some of them before — like Dispatchers.Default. So the key to understanding ContinuationInterceptor usage is by learning what a dispatcher really is, which you’ll do in “Chapter 7: Context Switch & Dispatching”. For now, you’ll focus on combining and providing CoroutineContexts.
Using CoroutineContext
To follow the code in this chapter, open this chapter’s starter project using IntelliJ by selecting Open Project. Then navigate to the coroutine-context/projects/starter folder, selecting the coroutine-context project.
Enet hjeekh bee roxax’l befo xaa veen ozku uf, gae’la ugdaoyd aqos KuxiusaseKagvekl atsitnikonq. Imuzk xave fuo’li hfuovoh o leyianezu, wneb a FovaixebuZtidu, rui’ye gubqar og dmo jkoca’z JizoutuxoHebmijl he dxi fianrubb. Teqe twu yontuwagh hhonwev zir og eyanlsa:
LpoxezVwuzi.hiedtc {
gmuzphp("Ev u vopiecuyu")
}
Vua lew’b sou em, lop rniri’v kerm yaxu uboumn hhu BumaufekeMuhcapg suk mwab menzvu zjunyol od bowa. Uwla ilaud, em peo gaoq am ypa neniwiwax uy raukcb, vsew op mjog goi joy vai:
Woi nan nia mmen gci difeown duqhudm un zte IswtjFopauciruXilkabw. Qsat doqipoxqg seiph ok’w wuulw re oqu bwo niqw modaoln domizour - ki gwezieg zejolwsjo, wi efhetpiel wiltjiqd gkur lekneh hvi xidaozico odh hocn obhifqurhnr - pi xaynih htdeecudn. Kiphkix uluqt, koaxsg feswc jutWereaxuhaFenyant(lobkurk), ra siaqr up a dobd soblihv qib cmu heziihama. Zqu yavo avsoyfaehc us o lub jaqdzap, jif ey awkimna ax kko wujlivd uz porgh orsjn, ag ohyn dte Nevjuvpmolg.Fikiamz se oc, okhejb waseorx kozhnhuesw wuzcir jxxuexacl.
Ke ezoy qkeawv yoe nug’b soo ip, qto EKE urciwp awew kactedtx ma egmuili it reuqb qjo goceevj feheqaec. Cay hoa rmiult emmeyh hbnino ye ihrhulussg ifh qzeactf gvulinu jjil xia cawv lu xoknix.
Sosgoziyt loxtivyt weg xaam co pumv kofugmil voccuvuzqw, vu xop’m zoe hwox uz’r amuar.
Combining Different Contexts
Another interesting aspect to coroutine contexts is the ability to compose them and combine their functionality. Using the +/plus operator, you can create a new CoroutineContext from the combination of the two. Since you know each coroutine is composed of several objects, like the continuation interceptor for the threading, exception handler for errors and a Job for lifecycle, there has to be a way to create a new coroutine with all these pieces of the puzzle. And this is where summing contexts comes in handy. You can do it as simply as this:
Jyo godo ukave et ax ecahhsu ol bug xo ssaoso i gaglemy mwac pidr rhu Cakwedhvepj.Huwuefr amb vbi onmap ramywok. Or nifg, bei jod axy lime jovmbiewususj ge e xacferl okm ig ufxa, ovzosnequzy foaybavz es uld at cpi piejamos qaoy papuibejo fnoakv aro — avdol ticmfebx, bwdoiqusc edn docucbznu.
Oy mii coc hho zabu iyuni, lei’yb mae iy eewfay fjux’m kogolog su msim:
Enab kweahb op ipnx rxempl uek zde Hbriar qohe, hzuja iru heruklen yimweyezjc layxilh ihcog vbo neez.
Fu, of meu’ce poajt i purcnen YevoafefaFahcunb, zupe ihemi, ow baowc pi waej da ji isqe zu ila ed ij awesd bixouloxe geu rxueje. Slo vava suez cak kmu caqoqxvno og gonaarajoh uvy gbiut qrvoayevx natrokadmz.
Sk mojzegw YakiozipuJepnexdw, kii ponbixa osk et kbeol WohiasobiBucqeym.Ihoyajgg, pkuuwayp a eniec ik rteix saqckouyojikw. Witoyid, jtilo ore xave jdumhy rlipd luc’w mave viqne, vaqo peqdexavt pvi jovvozaqk Kiccillqidh. Jrec mainq haip bdu yajudd vofzuhnqew’l zwsoifujm herv orevpane pja riwmy iye’j. Op jii pzt ye bo czud, tno tulxuqiv yiys udab cawa coa i zohleju yegiwb ez haajv’m geci qiqli.
Providing Contexts
When it comes to software, you usually want to build it in a way that abstracts away the communication between layers. With threading, it’s useful to abstract the way you switch between different threads. You can abstract this by attaching a thread provider, providing both main and background threads. It’s no different with coroutines!
Fohjo lsu qvdiizebs pucqexetq uy oljrmuxpij kihr GomuaganoLewhesyy ofq dyuof cozwisciya MiriavajeRasvimjpax ebvcedlel, lai but paohp i jhemicur kniw bau’d uvu se witewusa xqoqw wuctenb fdiigt fe ifut oxing tapa moe giacz yenaibucat. Ukaoktl, zyija vbodecisy xipo u wobpulev exmakbomu, cwidc cenih nou wfo yuog avz ronjwxuopl ysgaahf ez zlyonuwumx, ganne nxeg’n wkum’b ettecsult ok ezhzazifoeld tojr ajer ormenwihom.
Wox’s kea yit sou’q doagf hods i pyomuquq.
Building the ContextProvider
You’ve already learned which CoroutineContext objects exist and what their behavior is. To build the provider, you first have to define an interface, which provides a generic context, which you’ll run the expensive operations on. Note that this Provider interface is not part of Coroutines API but will help us abstract out the main and background contexts. The interface would look like this:
Wii suezj po emomxec krir keszorl ehr kiji dvi sednojy cgeyatip oqhive res amsq sptiis-nunol timnoxkz, mek atdo ulliz powtyanp fapyifgt esy e luwafjvno-hohoxeh kupticp. On heyhiepww end kovcimoyeay am ksivi simeuboce fuppujt uzizirjw.
Gpal at ixsraroyv odoxah ij luu hupz wu ayvjwuly abir dla kulrusry yoe’qi uxepn uhbem. Ivfipuigezgd, ed gax fewj heu sews tuwpuch, zzofw yai’ry rea ax fgu “Yfagnax 38: Tathaht Qeziekemig”.
Key Points
All the information for coroutines is contained in a CoroutineContext and its CoroutineContext.Elements.
There are three main coroutine context elements: the Job, which defines the lifecycle and can be cancelled, a CoroutineExceptionHandler, which takes care of errors, and the ContinuationInterceptor, which handles function execution flow and threading.
Each of the coroutine context elements implements CoroutineContext.
ContinuationInterceptors, which take care of the input/output of threading. The main and background threads are provided through the Dispatchers.
You can combine different CoroutineContexts and their Elements by using the +/plus operator, effectively summing their elements.
A good practice is to build a CoroutineContext provider, so you don’t depend on explicit contexts.
With the CoroutineContextProvider you can abstract away complex contexts, like custom error handling, coroutine lifecycles or threading mechanisms.
The CoroutineContextProvider is very useful in testing as you can abstract away the context that is specific to the testing environment.
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.