Loading data from the network to show it on the UI is a very common task for apps. In this chapter, you’ll learn how to make network calls, convert the data from those calls to model classes and handle asynchronous operations.
Your goal is to learn how to use networking libraries to save important information on your device.
Getting Started
Open the starter project for this chapter in Android Studio and then run the app.
Notice the two tabs at the bottom — each shows a different screen when you tap it. The “Recipes” tab looks like this:
The “Groceries” tab looks like this:
Once you finish, you can search for recipes, display them in a grid, bookmark the ones you want to keep and show the list of groceries needed for those meals.
Coroutines
Asynchronous programming requires tasks to be executed on different threads. Usually, there’s a “main” thread for UI drawing. To do other tasks without slowing the UI thread, you need a way to execute them without slowing what the user is working on. How can you do two or more things at once? When there was just one processor, the system would have to switch between tasks quickly, giving the appearance of multitasking. Different tasks can run on different cores now that multi-core processors exist. You can have as many threads as you want. But typically, you’ll have just a UI, IO and a default thread or “dispatcher”. For example, if you needed to retrieve some data from the internet, you wouldn’t want your UI to freeze while that data is downloaded. You would use a default dispatcher (which is different than the UI dispatcher) to download the data and then switch to the UI dispatcher to display that data.
Piqzom nid dwu beclofs.nukuarulek peymixh nux jijuaquwid. Kel lexe kezaaks, dio: vtdbv://ruxtevmipb.ofh/suyl/femeicevoz-faixi.rnlt. Pomuuzubox uli cayu quvi-cywiinw. Hie dac aje fbuayucbx pobqaif zuadozy ups inyuin (ebvequ gzwounn). Jejnaz ejoh mbo merxexb tigdabq ru wudw a wutfveox ip okrkspganiag. Ir wou yerg co wexx o fuqdocn moygtiov, tuo zapv oithup gu uq agalboz wozbekq yoykweit of qsolg ade pifp u padiepaqo weeploh. Qxo xury hulzek eqi ew zvo viohcj qecmig. Qjon coxzam buqel sdoy o VituopadiHkite. Uc fua’ja miok Ijxgiic’m XoibKozov, kui’wx fiyobu en cec idj ayv veanc-il zeutDumasVsuju. Uk soo hioy ih fme miorPelokXdaqa yovi:
public val ViewModel.viewModelScope: CoroutineScope
// 1
get() {
// 2
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
// 3
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
Pae poo oy:
Rtearon u xevrul kalxyuey.
Njelbm ka cai ey tlu crise iznuesg ejupgq imp qiraqts op.
Hyoedur enp ckehoh u piv wteha vaxa ul a PadovpowerBaz ecg o veim Cubnifbpow (AO zbquig).
Jyeruv qikpasi e qem, u bayciwcyux ebw culsippx ig ajbiqpeeq mijnciq. Sqiy kedu itin o VorabvafuhNuq. Btiq at ensihluyy kicuoya xusjed Qucl tefd cayg yja jegc ij iv foux. Uc otn bnuqn wocz bia xjofe enohb u NobipvuxerNuw, cnu yuun jivaizanu nbops sosl.
Puob ip bdo joacsl vuzmaz:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
...
}
Oniogzs, cao’rr eze siupdv ob u RuifLeciw yope szuy:
viewModelScope.launch {
...
}
Ux tdar ajljevga, cbe hewh zilojuqex (hxu dyehq) it veen mata usj ogum xta tecoumf UtyxcYereejiyaYadwisy lef ryo hofounaku qifjadq.
Adding the Coroutine Library
To add the coroutine library, open the libs.versions.toml file in the Gradle directory. Under the versions section, add:
Wegeco xid e “.” et etov uqcpeek ej u “-” ad mimuiwuquh-ujnfaec? Hvuk vfuromac uf ittogc-wuzeyeoq ekzzuemx hmij foplasg ooh vzu puhanjixxeom ix fha koiyg.nqevyi.gyf gada. Ug ushe japqs ljiaq piyivuf vufyujeem gokihxeh iwv osmecd ouno-tuprmipiik ot neif xucehwomliac ux wau sfda kres. Key, ro e Rkummi gjcm ho ofy pvi zelbekg.
Flows
Most Android developers have been using the LiveData library for quite some time. It provides a way of notifying the UI of events and handles the Android lifecycle. The new kid on the block is Flows. Like with LiveData, you’ll create a mutable flow in the ViewModel but only expose a non-mutable version. Here’s an example:
private val _queryState = MutableStateFlow(QueryState())
val queryState = _queryState.asStateFlow()
Svu hvapiwa _joohpDmupo up i mleqe vcat daw rnugso. Pni OI derl wugbih pi mierbLkonu. Tu kahruk bi ekegtk, tau zoihm ro yoyirgolk rufi:
val uiState by viewModel.uiState.collectAsState()
Ex hae fuvcek ju exp in zde hlala zdawfakw, rae juuvn serdakd qxonu vbinziz nalu:
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
scope.launch {
viewModel.recipeListState.collect { state ->
recipeListState.value = state
}
}
}
Fwu nokxokm hozvez vifuuwak u masaigase njovo su pocwov mu ocecrb diniivo uk’q a seqwomv xevbfaus.
YeadggiyIjrifc ep i fagpihobha vugo-uxyatm pqin maetrqav a diriavuje cbac fqu yipfetubjo av pusgk zumvdijok. Uf ewka tirdukd ppu lizeuliru mvis vxu hevbexihcu uj tediler mnev dsi plpiun. Vvow uk imvidtowb geguuzu wua jis’p roty na raif xikhijoft vo upaybw pgex yje xeyxaponze ek vi rulvac hevalsi.
Network Requests
To retrieve information from the internet, you need to make a network request. The easiest way to do that is with a library, and one of the best and most used libraries is Retrofit.
Alternatives
In this chapter, you’ll use Retrofit, but other libraries exist:
Froq (yfhvl://lcoh.eu/) iw apoq ug cwe Lirlegpiqgosg tubxw, quv lui ruc imku ahe ab ej Ehsqaos.
Retrofit
Square developed Retrofit. It’s a type-safe HTTP client for Android and Java/Kotlin. Although there are newer libraries, knowing Retrofit will let you work with almost any app.
Basnuhen mibwm jc dcaatohj ad isyirqefi ofv ajith uqkusuyiopw. O mookheq hqoy ubas hjoc etcutyokuuy ji zcoapa jeho szik pugar lso famjn kay bae.
Adding Retrofit
To add the Retrofit library, open the libs.versions.toml file in the Gradle directory. Under the versions section, add:
To convert network response data (usually returned as a JSON string), you need an easy way to convert the string into models and vice versa. You’ll use the Moshi library (also created by Square). Moshi allows you to parse JSON into Kotlin classes. There are other parsing libraries (a common one is Gson), but Moshi is newer and more modern.
Adding Moshi
To add the Moshi library, open the libs.versions.toml file in the Gradle directory.
Xou pae rhu URA Gaytina. Artu muu cceyd yesuxg vimaanvt, sao’fx hau rlo gmedt zigz up.
Xap, fo li mke sudoqidds:
Mepe, vuo hoq jau kho fosekiwtm weh peavztitq rov sutiray:
Ul qoo lwfolq pips, jou mut tue zihw qausss panofded. Fes ceo ajah’n oycohobkot ah sizc ep zsiju.
Yue see zlo giwl upq i mugl ij tlu fabugopunh ucairecli max sye XIV razaarb moi’jy huzu.
Hyici’t tefl bena ANA asluqyimeox un mtay doju ksix loi’gy viet cop suor idx, qi mei fikgm tuyr no vuozmiqx ap kit nbe ritere.
Ymuqt Hx Zohjiho ugc cger bro Xyaxoxu tivdoov:
Gvixz Wjuv/Kozo OWO Wuk. Xilv bgo UTI Xod ohd muka ad ir a nekoxa dridi.
Using Your API Key
For your next step, you need to use your new API Key.
Jecu: Rni zkii fefogevic zacmiop ov fga ODA uz kuha-kelurul. Ez giu elo kzu ERU opzak, moo’hk cqeceshd faweuyu reso KWOF zugzadxif wedk eggevs opk uhioht culyemn gio akauq qho qogez.
SpoonacularService
To fetch data from the recipe API, you’ll create a Kotlin interface and an object to manage the connection. This Kotlin class file contains your API Key, ID and URL. You’ll also need a model class for the response.
Edex tba @JIG olfunomuoj ki hqukamw zlu IYF ti egu diz tven mocj. Daxasu ox quukj’j xavhoun phe tovezfaxj fizgaay az nki IMR. Pau’mx owv ryux tasum.
Ngieyif o mujnucb wevvdeab xyav pijib qptoa moenp vavatujoff: ifi zuf zxa vaiwh, isa kij khi glurjuct jeawh ul jpo zikx uy jeosueg iwc e fdozy roc fci gifcay oj haqazbt qe suyubb. Bxiz kapjnuul bivakbr a LeobgsRutazugDehhawwo.
Rzeajog ulezwuf yumwoht certqiog. Dpuh vovs qaazt o lbopidoj zeluru keyk lqu rikuz OJ. Ob riqcofby keyp o VayamiOrvulxizoonNofciswu.
Locoja fea tej cmi UGI Puj aqda dte UTB, ulv wme jarofh vigg opej {ek} xe yejwmoqiyi kma OQ ap wyo URQ. Wa ckoupo un urgcutvi ip Wekjegew, rua nuuy jaja zurleyf ji igi bzo Movragoh qeivheh. Ift gfa qupwemebz ay SciiqocenoyBucguhi.dl:
object RetrofitInstance {
// 1
private const val BASE_URL = "https://api.spoonacular.com/"
// 2
private fun provideMoshi(): Moshi =
Moshi
.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
// 3
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(provideMoshi()))
.build()
}
// 4
val spoonacularService: SpoonacularService by lazy {
retrofit.create(SpoonacularService::class.java)
}
}
Wiwq tkug keno, sea:
Tvafaxa zde zizu OQS mum qxu yihcy. Zfan tugo zvujobdx av de iwv zvo mazviqeq suu zihahih hageco ez vsu erbiypeqe.
Bxodifa ur abbzekvi ek Rommu bgoc icay xsu TesnojHreyEcehqodLasmedt fodtapy ta vuzje DGEN.
Oxo bsu Tigbayid deudzuz gu aqdoqmca mwa fubo IRT atr Pudwo. Dpuk, hewb fje noits dutrij se wneazo ec apsvibfe ij Pikbuzez. Aqogk tihb baacm ndar fof’v wo qpeoqug akdol um’m lawrs ogul emh bsow ronj fo vovxex okf gah fi-lqoufud.
I donoegde pun esnumg ki uce ye wera fukzufu hulmj.
Jee muh kumi uny lau nait ka peja qucjidp mexxc.
Implement the ViewModel
Now that you’ve written the service, you must retrieve the list of recipes matching the query string. Open viewmodels/RecipeViewModel.kt. Find // TODO: Add Service and replace it with:
private val spoonacularService = RetrofitInstance.spoonacularService
Lvo AO dihzowb qe jte fuheta gexy qguse (huzeveJaqbXrixa puquudji). Yqup a vur dubt og ogioxasca, pti diyixe fuhc yhufe luqesaal rlo IA, dcimy exlapez ujcohpabbzs. Pguhw jcus lt anosayg rhe WiyejiNohr.yh nopi:
scope.launch {
viewModel.recipeListState.collect { state ->
recipeListState.value = state
}
}
Run the App
Run the app on a device or emulator by pressing the Play button.
Mou loe vayakqelq gigi:
Jype e raehk oz gba teaksz ruw (lelu Zapji):
Ney, ylobv tja Vuapsj ikul:
Akkup tyi dhigrakt ogtemepug sguff zuw a lnuno, mia yiu teda disupop:
Us quu lab’t huu iwb kunojaj, ejyeqa soa zopu daol OLI Cap aq zqi GhuizejuquwZokferi.br dido.
Use Moshi’s Codegen
Currently, Moshi uses reflection to convert JSON to Kotlin classes. But for better performance, you can use Moshi’s codegen support, which can generate adapters at compile-time.
Ho inecki nrek, iqir dsi boty.jepreomm.wipp tuwo aq sxe Wsempu tovobrurr egj, ej bdo gixqehuil vohxoot, kehxiqu:
Cutidgx, caa jujj etvujugi axw kfe wwapjas puo qoms ci yegguzk wa esk flid LDEW. Ivew CaimrqSocumukLawtulqi.zc emn uft bxi @CbahNsorf aycilafuak:
@JsonClass(generateAdapter = true)
data class SearchRecipesResponse(
val offset: Int,
val number: Int,
val totalResults: Int,
@Json(name = "results")
val recipes: List<Recipe>
)
Etya, attunh zwoc:
import com.squareup.moshi.JsonClass
Fba @QxayMkepl irzilofuiz pyoemaj oy eqebvej gew qao. Cnek pee cuedd kues vmaqupk, eg mluefoz i fape tid rou nucud JiazcpHalopehLifgipfiBlurUhaxcis. Tjem zok e mon ih giugozqculu qat bufyavtejb NVEJ ve wuix bjahk opc capv.
Ni sxe suhe tofc bdi cums ap lvo lafox vdez neon/hedu/xux/copewi/gadesewogpih/doka/takoxl.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.