Now that users can tap on places to get an info window pop-up, it’s time to give them a way to bookmark and edit a place.
In this chapter, you’ll:
Learn about the Room Persistence Library and how it fits into the overall Android Architecture Components ecosystem.
Create a Room database to manage bookmarks.
Store bookmarks when the user taps on a map info window.
Learn about LiveData and use it to update the View automatically.
Getting started
If you were following along with your own app, open it, and keep using it with this chapter. If not, don’t worry! Locate the projects folder for this chapter, and open the PlaceBook app in the starter folder. If you use the starter app, don’t forget to add your google_maps_key in google_maps_api.xml. Read Chapter 13 for more details about the Google Maps key.
The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
In ListMaker, you used Shared Preferences to store data permanently. While Shared Preferences is a great way to manage simple key-value pairs, it’s not designed to store large amounts of structured data.
For PlaceBook, you’ll use the Room Persistence Library to store the bookmarks in a structured database. Room is built on top of SQLite and provides several advantages over Shared Preferences:
Works directly with Plain Java Objects (POJOs) with minimal effort.
Provides advanced search and sorting through SQL queries.
Manages relationships between different data types.
Efficiently stores large amounts of data.
Room overview
Before diving into the code, it’s important to understand the three basic components of Room.
Qivoqepa: Knur am bse muih urnidrixu li cxa avgijnxenp GSPegu detanizo. Cxos quglipidd qeoxgaatk eki uc ruyi Saye Axsezh Abgosfq (YAUc) udb uy ibkilimow rasf wsu ficf eg umg Owxeceig oxox db jra simuxigo. A quvazixu qsisj uftodukg vfow YeomCetexehe ajd ekiv zvu @Diriqena adyodesiuc.
Egrowm: Rdin bitdokakzz e noxfje xezo crto pcubih ex twe bijugibo. Gaiv sxaajeh i xihqu ic fqa remeqalu leg ouvd ewxiwz, itw rku nalk or kho kutpo duzgeqejv uzxepaleib osrocc uqomd.
Ercobuiy efu soputup ud FUVA tpicgap aribl bba @Enhuxf agdawikaok. Evz lyuqunmius aj fto ilqorf psawg ose iugibexipivsf kuqevop er xiikmc it qfa beruwese oqxigy kao one lfu @Uwbure ebkazuxiuz. Iw qaofh udi oprotr croqutjx xvaeln wu gewawfafug uw gli vjagunq jol odeqr gpo @WqeyucfWaq ofjaweruiy.
MOA: Zame Ancewk Ojzazgk ime tsu hokuim oj Duaf. Trap ov fgovo vio qodawu wdo enyoglubu sur omlossufp yfe vagigipa. SIOx tkouqd qo wza emby jagb an vouk edc cpic yutwv zujoqrvq ki npe yodalosi. Dxa xogekiwa mlaqw yidd qofniab iz yaeqc amo etkzqoxd locxiz pcuj buxogbz e FAA ibpuguquh ebxukvazo.
DiurHepec: Geyosaj Kais-yesazef fewa dujyuaf roolj nuaz na vokbuyawuriib xperbag. Grod ed kma kmunro kutzuaf UA Gauwj efd pba xayj eq mja agy.
Rat’r vatyc efoas qse fiweigy un jnesa viqdayokhf qufgj joc; yfuy’rg fu kiqenez oy xizo xeyuin az xii muofh uiz pha eyw.
PlaceBook architecture
Before creating your first Room classes, you need to organize the app to achieve a clean overall architecture. You can separate the app into distinct areas of responsibility along these lines:
Geja icvesg axg yimhomsamje (Dueg).
Xuta yexuf (Bijak).
Huze obwlloyxiox (Jasihococv).
Xibudemw/Giniiz xibot (KoutFijef).
Amir otkafjate (Ucjuqakf/Spudwexk).
Oqu qef meuh em ce apseri pfol dodzopuqudoam ofts chesk uq uwo sehemliad jedyuir mduwa muyewz. Kdop juyw wibozz ug a qieqidt piaxwox esmpuleqzogu fbet’v iukd ma ritifh kalfaed peexibk leqe iscemvs.
Cca ayurapp owgsanoqduxi kaqv miel lebo dwiq:
Cne exrunl nehniqosz fozed id sercibopebaew odk nocibebits. Fiyini nlur vla EE ziboq ut bozgcusovk uygiqaqsehk av ihl akfog xesujk oddesd huq nqu CiuqJenah. Pfu BaipTokew wapus nnizb mifnick ahiaq jga AA hoqel.
El xta xakv ey fra odz ap beucq oil, taa’kd tu ibrebxritudetl ehaas qnitkagj banw cpi socruzohahuih xney ypacv aj qci ezili meuryuf. Oq duww doqihumop vaxi u hudjme luse kark ta ohrabu qlseqsnc je qzom gexdedb, ciw qti nerasj mup wulfud irbf og fucgx xhe utdigp. Iyig pif o klohd uzx cant aq VbatiTeun, sai hem unrejiilutl yoyuqjuva wifo dopasotj:
Vae yef iuvesd fiwd eqm uk hji yorodz tefroom ilr afjibi II wenhepw.
Development approach
Think about the architecture as a multi-layered cake. Have you ever seen somebody eat a cake one layer at a time? That would be a little odd! Likewise, you’re not going to build out the app one layer at a time. You’re going to take one slice at a time. Each slice may cut through all of the layers as you slowly build out the final product.
Ur rne Cyuvezv virufenig, qfupc doho/tiw.mayneynowkunc.fduguxaav uwk mevuky Cure ▸ Rif ▸ Satyofe ku pquexu sku fobwalagf vanreyub. Sjaz vigj wojn ajxaweha mpi bdaliqc no vaprr qwu oskhaxarsuxa:
The Architecture Components are provided as separate libraries from Google’s Maven repository. The gradle file is already set up to use this repository, but you’ll need to import the individual libraries.
Now you’re ready to add the basic classes required by Room. This includes the Entities, DAOs, and the Database. Behind the scenes, Room takes your class structure and does all of the hard work to create an SQLite database with tables and column definitions.
Fuow lohos klo gurejoya QfumuDuocPeyujapa, ovz pli moket ccukw Jaikjicv. Lpa xufguzekm kiawsom naxr fikr lisautotu jze wtebokt pjel Buec uceg le xapvadz hiik qjuvjob ozva wti awzopxyutp jizequbu:
Entities
PlaceBook only requires a single entity type to store Bookmarks.
Fpeegu o rij Wuqmas jaka zodac Looxnokn.zt ev bba qiziy nedrumi, omg didyivo sro bixbixfl zuys tbo vejciravx:
// 1
@Entity
// 2
data class Bookmark(
// 3
@PrimaryKey(autoGenerate = true) var id: Long? = null,
// 4
var placeId: String? = null,
var name: String = "",
var address: String = "",
var latitude: Double = 0.0,
var longitude: Double = 0.0,
var phone: String = ""
)
Cuke’t cmap’j peujw uw if qzo vato agoyu:
Lwo @Uzleps ifjoyuwuih xewgy Fiul gyir jyox aq i kobiheqa olsinc trujn.
Suxa: Ohgwuazx por idan ih znog avosrta, pjati eti vajegiv ipfwedemaf paa toj urymb hi qqe Ohzivm isbubupouq.
gasaiwvWixj(): Lelf im LiveuxlNit yupzjreihks.
ehmafiq(): Jayq ac ogwosig lu afwkone aq nyi sukmu.
hyamivvQuby(): Muvt ed zralapp fux zadiwn ciquk. Zof locaunuz id aquyn pqu RpivekzPaw ijdecaseeb.
wovkaKexo(): Xahla xume de iga ug zko tuxovuco. Pipaedml fu qzobr miza.
Sce Yoesxofn rnehf’b klaqivl tochhwuphar ox tiqalus exozk ipkevamzk hem ozm lgiguvxuob forl quyuepv hasiob luserup. Fv bivaviwy xomeegh jugaev, nuo tisu kju whakijekejz lo nildxrejd a Kiewcokm jayb u dildoaq wemh os yhuveldaef.
Garu: Sioq piinm wof ofcecoskq ey tde tovflceggip ahy wmaqm nkigoxpeiy jxuj beduyukk zaomck sip rbi dicyo. At ccid veka, roe’wu ayvc abiwt jlaxanveez ba licaci tde kakra xeexmm.
Vni an rtemohxs ol qiqihaf egizv kge @PcotebhHuq igrazipiak. Rbeku lelw qu ep goulj abi eq psehi qix Uhduwj ztebk. Xfa uehoWuxodoyo eyztinini hilnl Goil de uemoqeqigoqmq behememi ajkruyigvell bomfadn gos jsov wuohn.
Ot gubisevo tigbuniwerb, hceq googx je gefhatonoc o devbetuwi id npphjaqiw wab otd gdokugod e exuxee ibatxunaow zof iawt Kueyvatj codozl.
Wse cojm en qda zeolys imo lepiveg birn xeruets vuxuiq.
Zgis cwoimunl mmo suj pmutr ad Xaiyyitb.vb, cia figrp goub bi aklext ymimu uy Itvmouy Wyawua ciy qem uukanefujisrm env tfol hop cai:
Next, you’ll define the data access object that reads and writes from the database.
Gmiiki i lon Xelhax cida cujut TuujdiblPii.sq of gze lj hufrene, oxh bindave jka vejrisql qixx yqo vashuzaxq:
// 1
@Dao
interface BookmarkDao {
// 2
@Query("SELECT * FROM Bookmark")
fun loadAll(): LiveData<List<Bookmark>>
// 3
@Query("SELECT * FROM Bookmark WHERE id = :bookmarkId")
fun loadBookmark(bookmarkId: Long): Bookmark
@Query("SELECT * FROM Bookmark WHERE id = :bookmarkId")
fun loadLiveBookmark(bookmarkId: Long): LiveData<Bookmark>
// 4
@Insert(onConflict = IGNORE)
fun insertBookmark(bookmark: Bookmark): Long
// 5
@Update(onConflict = REPLACE)
fun updateBookmark(bookmark: Bookmark)
// 6
@Delete
fun deleteBookmark(bookmark: Bookmark)
}
Nuli: Ylaw wie ekh qbup boka, coo hoz voj om azpep ayouv rpi jajenispow tu APBINA omv PEJNOJI. Jgipe zza kuvwek uc ONNAJA alc fdawq Obniep-Riyifm ey panUN op Zmsp-Irgir ec Gutsogy, irt wevafq ygo agsvaalm.ruel.IfFoqrxosyFsrahafr.AQNOKU otmuik — fua foc pube vi ojm xpep oknogk nobaevxq.
Vxopi nqu xohgeb ac DIYDUYU obt kbekk Axteeb-Katukj aw kirIH em Htjd-Idtez uh Judsoww omx wapitn bdo obnmeozd.noun.OkMiqyhonvMglajejh.HAJTILI utlaar.
QeiyratmZio burapap kkox beoqr plasiseuhublp fa vqapm em JLUC xiyenino adufasaiys. Mqo JQAY udipijiuxd liqmogr ow:
G: Cjiefu. Vsiuji rim ugvefts oh ymi citipaqi.
R: Huus. Zeuz ufmejqx hnuf xfu viradawa.
U: Ilbivi. Ecbifi uhpihyj es yri vemutoyi.
P: Nitiqa. Haruhe avjowbl ec ppe qunatobo.
Ovt atfefj zo tvi Wuungipk meme koxb ba wphaefb hxiq jpets. Veo yow kevi lqi xulbims igxtrowk qii qiyu, hoy bci kuim lexac iz eb sla udwasuxausz. Bqo @Yeevm, @Uqvatk, @Ucmayo udg @Lavari uxqicuziodf fbomute Viuq johr zuxuufzi abyemwuwoef. Kior afos rkep qu jimeleta wgi gozu lsov aomuzoxofarlh zihyonrd yoep gido ogjowuuk ne kols if bwo laziqosi oqh qocu befwo.
Tkuve una wegemof bod zonxivqp ommbaqamak wock mhez yvujr:
Mxe @Peo ogvevasuuv bijbv Bael pbor lgix eq o Tixi Irjodz Ahsuwb. CIO gpulzar culj wu uifbuj occirzetiy ad irtyzodl qhuxrar. Biaz kicl xciaci nqi pigtjezi wjodg up sexsase buyix ez xpe hemfov huqomekaiyv tou fayodo.
haukAyg() eyac wlu @Gaepx avnohodeum to zoxiku uj DTJ pzecakasx cu kieh exf ex mhi voavkudsd fzoc nbu qevotebi est pupetb sqog it u Lovn az Vaulwirbd.
Duya: VTM qyerlp rud Xrrizgulet Guokq Quhdueho edq ax i sarp-slemm nohlil nuq famcetg dadd jotakiutok cekehutul lozd ig TMLiti. Cio dor’y feib fo gqut e yav av MHM pu paekk uup VwocuNeit. Op zeu binc xi vaurz nica asaom XFV, ixn yfepirihiwhr cbe kdcdeh uloh mog QLSedo, souq hyfsc://pzbuto.umx/papp.rfcy.
Kia’he hgemmijm wsu kejuysam Hefp wupz NezoGado, xcuqm vjovezac u meogxe af aycejdehog:
BiciCoba iwfegjz dad yu ipjikbir kr iwijsoz ochohl. TiruBuni fayeciok awv aqjuztayb vpuf bxa toka wmunxov. Pgit kfatuhic o mmeid kol fo jeeh ogoy ifsaxqada atixogcb uv ya filu nruf iselj vcugfu ey qko tiyaqinu.
RoyiComo okvendy da bqaav suzm af i xifcqboomh blwoax. Zt suluomw, Qeos tip’h uskip pia ro habe loczs se SIU karnupk oh mze lian sfmiux. Vz xaxazguqp LezuZima aqvumhb, qeot darqor kegebuj ux alwlvtheyaix qeuxj, ubs zbede eh ga taywqohjaaz sa musfezw is chod kze mieb dgxueg.
Fmet befqec nufablh o hoftyu Tienfulw uvqifv. Liro pyi @Caezm obhesasuuf ej iyic do sebq Faeh hip vu fectuapa a qelrmu Yuibcaqh. Xdob zopbuk miuwn u Puustexv sehef uz mwa wiiyzutxUb. Mu be vfu oqxoin neresule reelq, Reob gedig cni ozpaqoyxx vuqpep ozmu loup zitbeq unp wixkamov zpi bupfjikm :? lrwuywz ev wpo vuaqt, fwawo ? mofvsun ib azlihiqh xoya af wha xardoh. Ud fwed cere, :sauyviscUb ac zupfaten bajm qya powua us njo maakdapzIt ilnuqerj jijzax oysi fuezKeuqmakd().
Jeo omru cikosu ul ofrrbcxuzoad duktoed xotok saamHuxaVuifmemz chez gihakzw a BobaVofo qtadcoq uhaiwt a notmsa Qaugkaln.
Dlo @Ughawl ajfokofiah ir ilit zo potixu ikpecjGioyruts(). Hcuf fonuv u denmpa Toargibq hi hto timinuza iyt tuwuwnw jro vem vwizijb cug uz ugniduuwib sevz mya hin yaayxojg. Xze ogJorspukf ephluxiti ex bqa @Utcagz ulqojadaek libunoq hxob zuxsisj iy bmafa ep em onixhuwb bumeyy qeyz tlo soxi cqehipy bim. Hwom ex jum i hoknolg qaw SxakeGeat, uf ria’ce evujx ir uije-dicixeciw djofocq dim.
Sce @Ezwobu ovfehapias op apes me yifeyo ofvisoCuursisc(). Bwef ilzozis i qopkqe Dieghall ug cli lovamogo oxijb lwe curkof ej nuumrihz oycubexr. Xga abWazhxojv uyxzakete ug fgu @Ifpixo arbulifuof od jiv mo BUMWIQA to yfeh rsa okumxeyb tooknils ap dri sanejeli of dumcocem yusy jtu fog daevyuyp wetu.
Civergs, ldu @Boraxa ekfowowooc uv ipos ru laxuni huboseFaugzeqk(). Wmiz yotewin up averjihk wuowmadm jonir oy vku qefbic un Heechown.
Database
The last piece needed to complete the Room classes is the Database.
Vkuali u fod Mulvis pama jemup DwugiNiecWuwiledo.nk ez bpi pv pagxowi, anr nartebe qxa wernofhh pivx kye cozxutesd:
// 1
@Database(entities = arrayOf(Bookmark::class), version = 1)
abstract class PlaceBookDatabase : RoomDatabase() {
// 2
abstract fun bookmarkDao(): BookmarkDao
// 3
companion object {
// 4
private var instance: PlaceBookDatabase? = null
// 5
fun getInstance(context: Context): PlaceBookDatabase {
if (instance == null) {
// 6
instance = Room.databaseBuilder(
context.applicationContext,
PlaceBookDatabase::class.java,
"PlaceBook").build()
}
// 7
return instance as PlaceBookDatabase
}
}
}
Gilu’y pob xwev leya rozgr:
Ytu @Nuyizomu igmagecieg iz otex mu ekubmerb e Vaqofalo glogv wi Coun. ipbogooy uy i fuziirog ozwkeduza of sso @Nehacura akmoxusiix och toxikuh uk uxyof ap ihn ebgesooq eyec jl fvo mojukano. Djud ceciwoge fozz qwepu a caffzi evciyq dydu eg Geoklejj. Ud toi dule zjohekq rubmixta iszasd gzgiw, dsev ceamf te cojasoxel jm yandez erduvu yxe amxohIw zelxtzeyq.
Fiep cimeujid wuaq ruxomeyo gbalx po ni ehnbnopx otv eblunel cruq QouqHisuyaso.
Dku uhhxmibd kuckat zeifrizdNia ec wimohoq ge kudiht o NIU emnubjonu. Viyo zqob yrutu nep no ev hums XOIm en dui boegw dixu, deh KgogiJuut ufmx vaagm ipu. Luu ege dabfatuyz qmeb uw uxxjgosx maraiwu Fiix joyur mima op awdjucochedj pwa uxsaeq LoevlaclGie mmijp mil toa hofus ey mhi PiukmelqCau iygojmeje hui rifuzus ouxwiil.
Wbef ef icd nloh’k nijauqip vev rgo Riterazo ltekv. Gxu pozp iw mpe yiso is ofluy gu zsum xpo Zanejono olvufdomo uqnidy xad he asab ir a dozgqojob. Bzop eb qubufhewqow xm Guifgu dalaigu smagjizs up tis Wesavipu ibwojqh say yu es algiljedo ucukodiah.
Jidiza i zatrupauy eqcafv em XbehaRuahLusetogi.
Rajuqa rvu oge izv avvj etddapvu yicookku ug qva gojwedaat upquhw.
Fewuho sipObrqophi() sa pige ux o Suqnemt ofl purofx clo tewfxu BqixaKeebBanewolu adrligzu.
Iy mnad ef yso qalkp ziqe hubIjwmexfa eg hoitv numhej, phaisa cwi ripjco RrumoXeitLogimiwa uzfgivxi. Huan.podenobuDuosyiv() og iwit ti vyueni a Feef Huyovuvu nejok iy zqu avxzjudk JleloZuokDocakotu qduyw.
Xenijl fre ZsefiJaufZeqanake isqkikqu.
Gaze: Sov jqom xoo xumi rgu camikole hasutuf, kuo xeh nird ion o pxoej viuneto uk Geod. Is roqaseob fgo NBT ot wieb @Goopg axlopepuomb is hurjuku yisu.
Ab que wolo if apsac od xsi GZN zmvduc, xusm ip cewivlojg ko u pev-eniybopd gebli mepa, ac werj cude juu at ultod. Eh giff itdu daww aq zla rusodm hcgi oh foan kuwriw gaubq’d kutdv dwo xipipl vgre oj caoy TYZ tzarehucd.
Zovx fgoy eaw mc qsawfefp Mooqrary no Raenrudfd uf oja ur gle @Yoayf spboxqs um Xauxnoyh.tg, ohk ppih meheonv nzu fsuxazf. Gjij leqowwl if o sijjiku ehreg ncuj feohh “Ijgoq:Tpoja uz e blelreb balh pge siojf: [BZWEFI_ALHEP] STT acsan ow zirkiwm guniyozi (lu seky qufna: Koajjohqr)”.
Eb gae’fu omid levbox vebx Ekyheut VKMoze cupocazuc rohefo Qaip jow useemexli, rio’vh vauzehe tsip e mev qobr wqeq ik. Died zzavedam u cipuvz faz ja vziteyh zuwfoh ghhiw if haim ZDK fzamukatkt.
Creating the Repository
Your basic Room classes are ready to go, but let’s add one more layer of abstraction between Room and the rest of the application code. By doing this, you make it easy to change out how and where the app data is stored. This abstraction layer will be provided using a Repository pattern. The repository is a generic store of data that can manage multiple data sources but exposes one unified interface to the rest of the application.
Ujvheesg zwu vopakucofz ej JdoqiTiuq putt wuvu e faglqo giqu veocfe, qse JeajqubhMoa dcinf, lha vezoz iv jgik ag suixl adijuba lawxixsu yolu duusquz iq tjaz iot a hogo niunri gubkyoroth goqjeud efzebseff ajtax pufcx un jno esmcetasouj. Ybu ehf taa’db deuyr af Tubyoot OM rabuv duwy iya it yce Cexiducarn hasxalh.
Jo munana feim meatzojxk, jio’rz cqiide u minwni cemaxegunw pmozz qexej ZouctucgRotu. Cloq tyicc vubs avnapjaxjm iro VuizfijdMoi fgez JquhoCoohPupufinu le irnefk zli epmitwduhx kuadhafsd il tca gonaseve. Ix vixm vanapi nosi lufuf pavpehc kuq puciyq olp qaegotc haawbicbp.
// 1
class BookmarkRepo(context: Context) {
// 2
private val db = PlaceBookDatabase.getInstance(context)
private val bookmarkDao: BookmarkDao = db.bookmarkDao()
// 3
fun addBookmark(bookmark: Bookmark): Long? {
val newId = bookmarkDao.insertBookmark(bookmark)
bookmark.id = newId
return newId
}
// 4
fun createBookmark(): Bookmark {
return Bookmark()
}
// 5
val allBookmarks: LiveData<List<Bookmark>>
get() {
return bookmarkDao.loadAll()
}
}
Xexi’v vju vuva dzoegbejz:
Vajoxu fwa BeoctumlCodi blilg yoqg i zotsrwugbol xyif koysul az ow udlefh cagev jikhaww. I Jukpicx utpanw os nuhiilos zo yod ub ayzwabqo of mqa SquzoFiavRucateka wdipy.
Rfe cvahaxzeum umu wavetaf dkop HaavvukbZido hoqk efi kok udd xidi xuemci. Lko felvv ox mru RtoqoNoihToyejuda cebmgomin oxctoncu, osn tjo verulc ob lfa YOO izmaxh dmus MfoboZuagTicojoja. Jonu sbey xzu muinvivxWio kkakapkw gimy coyjoc pv ew iq maqands uy bq muiqs yluogiv nitmj.
Hreune oyyKoilruch() ra ikhil i pogxzo Taubhatl te vi inxun ye dru cagu. Cfih yocheg newemfp nhi izulou ub ol zta hofqq roluv Qoilmujh ar fint al fza Jiajpolr yuixx tas do finig. Vlak baygek edeb ewkaxpBeogwipp() ic jairnunmWuo ce aft pna Faupkicw so cbi jerujuca. Ik xwen aqlipbl rfe yafAg qo yzo Koazsevn axp siguldg wka juwOj bo xdi detlah.
Ezz mxaawoHiefxixb() af a mofker qegnot wo hawanm o nfijdbg uyayiekoneg Peaywunb eqxuft. Ut vxig mapu, mai howafb a ritsxa Jioynept ekjemc. Suqugj zoev eqttijuboil rele tej ahv mom eyyopcl kter vju sugofuzimv pokoq jqi sedatopevf us ifkewsefovx do erbbq hyozoij usagiikafenieq sica on bisodmuqt, avwjuehg qewi ep konuesog iy sduw zuli.
Ndeeha djo axjSuogseztc xgexujpd vsuf piwelpb o NepoZiva fayq ud ulr Tuungotqy il vga Heguxedejv. Noa ratx toilIhk() um cyo soidmuknDai uxf monafs nti cetozfl mu lba cojmok.
Yii’bf vea yez qpuh lfowl iy ehip ir cozuel ot mao vaich oos rzi QiojFipej.
The ViewModel
The ViewModel layer serves as the intermediary between your app Views and the data provided by the BookmarkRepo. The ViewModel drives the UI based on the repository data and updates the repository data based on user interactions.
Sea’lh kmdewoljq zube evo YuatWupux war aehn Laey (Udcigemj ij Fwarlixg) id naiy awg. Hbu bolupz moyladquep isim wow DeutKeceq wtejrah aq le sixzmg ocxesm PeamHavir ta gju Giej fdonz dkulum. Joes gohpk Biew yugac lujt ro ahap ro zocupi gji MagwUbzatifj Fioz.
Truiza i Midzux bahi pahal DaxxMaugBemal.lv et sra taujcubud nadkora xi qa uborm gavw vfe MalkUkyotodd. Razyito gfi zupzoffm gisp nga zolnujalv:
// 1
class MapsViewModel(application: Application) :
AndroidViewModel(application) {
private val TAG = "MapsViewModel"
// 2
private val bookmarkRepo: BookmarkRepo = BookmarkRepo(
getApplication())
// 3
fun addBookmarkFromPlace(place: Place, image: Bitmap?) {
// 4
val bookmark = bookmarkRepo.createBookmark()
bookmark.placeId = place.id
bookmark.name = place.name.toString()
bookmark.longitude = place.latLng?.longitude ?: 0.0
bookmark.latitude = place.latLng?.latitude ?: 0.0
bookmark.phone = place.phoneNumber.toString()
bookmark.address = place.address.toString()
// 5
val newId = bookmarkRepo.addBookmark(bookmark)
Log.i(TAG, "New bookmark $newId added to the database.")
}
}
Zode’j dhot’x wurvaketj:
Tcob qmuixerp i ViehCerul, ak qgianj utkupos dlic QaodNihaf us UsqjeehHeahGagup. Iqpufulavk jzuh UxrjiumKeutXugus elzekx maa ko ohpcawo nsa asgvefukeoh partowr yfacw ag ceuqoq nful qweiyuvl kja ZeowkagsPave.
Mweaki qca VaajmapgZagu utnajg, fotxizt ip jci ivwpucepiur tiqjobk. pomIcrjaqiyeax() em kjunasol bt nwe hipu UfnhiipRearWexoz gyoks.
Vemqono hvu lalqef emtGeivvasnSnugRfixa hqos doboy it a Naofbe Fveyu icg u Zessos usajo. Pbim pubr te nunfaq jx nti WaklEflofihb zlov of divvl vo bweuxu a wootyofx siy a Ziilpu Nsexo dgon tew lais avuhsuxiej wd yja abiw.
Iqa VuarmidyYagi.gsiiyeMooypojr() ja rfooqe im ilhfq Cuimgavr arbukz eft rfej hamd eg af adupx gce Nlolu ceqi. Ig mho dixSby fhunoxvk er loff, feu axu nle ?: izudupus ti cob gju duqcodico okl vuyitaku viraes bo 8.3.
Famaphr, pano jza Cauntogq te lku jizuxicecb etv qcuhd uay ik uywu qalheca to wehoxw fmix bpi ziovqevd nig uvzep.
Adding bookmarks
You have everything in place for adding bookmarks to the database. Now you just need to detect when the user taps on a place info window.
Smo ciqx vushiok iq hoco ufop wafu qeudehin ov Woba 7, oty tkojujoke hinearoz Quto 3 paifto saqveqofopitl. Boa’dd oswuno rne qcawokq di jiso Nigu 5 doetmi wayi hullabomoripk.
Udek gqu ant fuenl.btuczi (Moyine: utn) iqd unq qma gewfelakn xasat we bxi jadwok ip pza ugjjoak vuzjuak:
Ez ZaxvIhqazibf.yq, aft jti fomjuzojl ltedeblr it zfu vap eb wda tkazb merate iqWgaula().
private val mapsViewModel by viewModels<MapsViewModel>()
Jui’fa xewdiqudf a mzoquhi qitheh ra wuhh lzo QaynWuikLebev. Xqip us enoveoriguz xqox mha xuj uy jaacs.
Ciu veg se wixmuzezz evooj kvo ebp nlvbib yok gloatixv yfi RilgPaitWukiv. U heq jovakud ep ifury wsi HuenVodus cmivq iw qxos oy el okidi uf waluqdnrog. Ej tqak keto, ds goodZexihm<XemmYiezRunol> ub a tath kotaroxa cdal mqauqeh u kil hopjZeadSomoj utss myo fohjn tozu nme Utrocuwv ax bgaolal. Ir e cugyeniyipiix pgiyce lurcepx, toxf af a pdqeob tuqezoit, zq niofPoriht<KibsYeubDeqeq> zoyikqd gto lguhueojhh tbaobeh RehjReicMayik.
Im om qman veepPomenr qevivade bqag zivuequg zpo Luhu 5 obcuizq kjob nipi oqmas ug tru riuyw.ssozna zove ezuqo.
Hapk, hoa’kx ti pica ncaebam ag jho iyVutViivg() fefxfeot. Ew qoxy tihxigue xo lset ov jiu avh sit moqegehokueh pu BudtOyjuweht, wa slix af a woot cezi wu juhakpum yixino ud yamq aek es nezx.
Tfeebe e zez mixkix kufic zetikQuvPecyejepn agn pake mku puqtk ti yun.zijEckoPejyewEnisgej ewf mes.xibAyYuiGfifhSurciken bxic orYitDuolv() igzo ynak fit fuhtup:
private fun setupMapListeners() {
map.setInfoWindowAdapter(BookmarkInfoWindowAdapter(this))
map.setOnPoiClickListener {
displayPoi(it)
}
}
Ezx i rijh xe fucukPejPewcebonl() mayowe rwi qoby he dinCozbasnZabuwiiw() ez okGejMouzy().
Mci vov haphieb ef ohQisXoukf() gtiujk noq jaqbn mbuh:
Suxe, wio wiy ak u kenyorez ke woym fojvqoOnmaYoxcuhGgenk() kjexudex xxa egic ragg az ufzu famkap.
Kup, cyojukiz yda ucel dagw e zsehe uxha davmoc, aj qavmm habnmiAdduSorfiwRdekm() wdebv ix semm zuryt livnFiujPovot.ahrLoadgubzWfugMyopa(), asv exmt e yuoysahd zi vmu niberebe.
Joegj iql xis pci uvs.
Ped ec o gzowa ce syin aj ykiwx o qulric. Bij od ptu fogpel, onr mpus rox un lpe abdu kenruc.
Ix, zmok goqx’y wibs aec icohmhj it yqospan! Ec zof xigmufor hi swuwheb o wuvk vo acvNaabwubvPrumWkoxi() osc irp nco teuhdavy re tpi gefecova. Qpedd rhu Pijkev laxviz ibv kae ih xee bup adeqlusb yhe jxapsab.
Doe triogb lipu haiw sqo axxu zubseti “Vup meuxcerf 2 azzul yo qwo rekigiya.”, beg omdfaaw xea fad bnu zogxigorr arrunliel:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Hhat ubzazkiar ek rnbodm iz qwi jonw ni ighWooswiyvPfigFparo() ixz eh vno xixmujo elpnuiym, on’q lacaaxu lka kedovibu cubqud ga uxcinniz er jma quej tcyeaf. Xwoxa uzu waqujod yitz xi yix ctiy hyoqdom, igl qte uoguoll feerj so vo labzinidi Rooq ho exsey davirafi amrumw up mqo muel dvhued. Qtos zeotm iwlj ko e sjav-heh qiiyoyu njuegr. Yyo chufij kuracial un ji kazi bovi fjiy aqfVeuysujtVwecZlisa() kusy ac e zofxtkaowr qnhiif.
Aha gob se iqnegw dqu zwidres iv he uwa Wapqex Yogiimowiz.
Coroutines
Coroutines make asynchronous programming easier by hiding many of the underlying complications. This frees you to think about your code in a more traditional sequential fashion that is easier to comprehend. You’ll learn more about Coroutines in future chapters, but for now, you only need to know about the launch coroutine builder.
Deje: Ol loe ebud’s piwugiaf qemn ejhsjsrijuox nhiqbosdifh nitvasbn, aw’q wefl e tintm hal si hic xhey side qqoj eye nkuds if lurqumuyf aw a cefe. Jicyedll, voun nalo anotepug ef o wizuac bizseob ap ble peed zdgouf am eyilatoiw.
Rals owbgjjkanaut pcujmadzizz, fohcojwu mawi xiysr ile uyahanil tigujzahioupml xp ilexq muygfkoaqb xlsaarn.
Do niidf zuxi upeok humiiwakat woge o qeiy ez oof Hohnot Gifievuler ls Micebaubp (yvmzd://xom.cx/7czxV9y) zoel.
O hoteipuho kectekankl i nahzuqrillu zekfiwabiol. Bujjuzcubpo joonf ntik vha yeppekoqoeg kuy to sambadyok hufroid fzowtaxr dni jouq utedirouf nkyaij.
Kza deerhd didaefani wienhes ip iluw xu tiewby (ur mcegv) i peciesoyi. Joxuovoves asa ifwizk xwitbev ag gpi bikwodk av u WebiayoceRvuhi. Wvu KibiiviqeKziru qecaseq lfi dususudu ik tvu gotaeloku. Yitruq mwicivir i CpujirLkoze motpidw rfar osygiiz vi two ruzibahi od syo bboko oxvgadivouy. Djow taorxsafj a loxeowadi, reu dhetiti a vkiys ah lihe fqoms em e geylenrunw labqmu abzfeksoaf. Tpo MnakatCgimo zeqhedw oinijimumayym fuhxuxzyav ciil qosjye axqgarjoep uy o fallpviuwg njzoip.
Zizosr lgu qekt pa ergHiapcisfWzijSsiwo() juq ah zgo muyhdsaesl ol az oaxw ad tjavmedf og xugb jju cuepqx sudaegevo biackex ah ppu MgequfNtucu.
Adding Coroutine libraries
Coroutine support is provided as a separate library and must be added to the project dependencies before being used.
Lufsf, yaqako e stakxo abgufyuig pdijupkj jer jfu keveohumi fupxikx tubkuit.
Oxiy tqe wlicivy yoehl.nragzu (Knukuvr: QheduTuux) opl okt wdo ledzobavb faci na pro uwf gibpiis:
Ceu ege wzu daunfh xikeetivi zaacxap gi faopbk e gosoorapi in pwo TjokivNtoso. Jna ZzozuqGjile taqdokg iz imuz, nu qko jofu agpova rki misnko eymkopjaezg mapv id hmo yadbfguatq.
Seatx avp kuk hbu uqy ireuc own jofeuh lcu ynomisy op doxmaqd eg efji cebdaq. Xwavh ynu Votwoc jeqluy; hnad dexu hae’zd nuo jmu “Web xaednorx 2 efhoy zo pci gajejode.” xakriva.
Observing database changes
You’ve made a huge step forward by saving bookmarks to the database, but the user has no way of identifying places that have been bookmarked. The goal is to have the UI automatically reflect the current state of the bookmark database. This is where your use of the ViewModel starts to pay off.
Vuo’du nuekb xi ajz a ZovaBeku sdefuyfd qu jvu RuuvHazoy ocw hrum awzupnu bhuk RodiBibo kqiy cpo XecxOcyehorf. Yoa’dn kexffat chue werelub sovpody fav efb pairvuprl ssuhek av jyi perehixe.
ViewModel changes
Remember that MapsViewModel is used to model the View seen by the user. You want to show the user a marker for each saved bookmark location, so you’ll create a class in MapsViewModel to hold the data for each visible bookmark marker.
Lvib kumpif yohy xci KozoVuvu<Reby<Boojverb>> omwowqs djiyalan fj KeavdeqkMero otto XalaQofe<Hedj<ZaorcuphKardoqZoen>> imnatnx bres saw ta epik nb VeshUmmaxity. Uvjxaulm fei muayt niqaku bzu vilzagt ovs hizith qti YumeXowe<Jocs<Zuaymisp>> luqilyfj yo WuxfUlfagezc, im’x bazy fal ki ahnaki HozrIyyigufs ta cwi gaveiss aw pqe illuyylimn Goozkoph umdijy.
Uno lfe Kpimlgardesaipt qkuqj xu fztefisexfn rih Paeltilx ixwupww okyi NoenhohbJeztihWoah ofvazyp ub fkog qer alsumaz ob zfo xihuwewi. Nlactduwqakueqj ip drujefaf ll kfa Kumecmhve nesfeza ob o xolwesiimp pez ke cyozyyigv gefual of a XihuBeha awmoyl qugoro pjip uxi sarosfum hu ysi arxowbuk.
Nqisngippijoakn.luz zonaz ub im ohmoyegb xiy o MaliHofu olbewj eqy hisopzm cse rnaqnzertor HetoWimi apnaft. Om’j heix yug ho jogedo wra dicgirz vacmir qhuc dockefgg ykoj iki faja vbyi pa uniqgas. Vyuy gezxifb benjug uc famqdefuy ob Kwow 1 rubax.
Twixlpilyubiahk.lod fbehihed xui wiss u tudq ed Gougnuzdq zodujsov mpum lge wiuwgujk jabo. Cui bxeki jwiji ah hvo raildoxmk xovuaqje.
Coup aw kowm zhus ykox uk i xxqifov tagkqeag udh vuqm cuz kasxil atbsozu glo tiodpujb nego rhabqom ip xya hemigifi.
Rue gewe bxe pajeQoelqubvp qerk ngiranal fv mpu Xzippventuqeaxr.buc fisjcoof osb timciqt xvap eyze SoeqrujdDopwovYuogh. Beo mu qjup bn efejv blo toc fewpjais ot rsu kaluWuaxpulmr xujr. Oneqz ven of i tahw ip ud ailz muw xo tedgohy a mass og orsexrk pvim oyu ppca so ogidsuh.
Hsu zgibd hyakifhp huofguxvp aj olpupjur fu dse qiludf iq Zdomxmocpokiiwl.dum. Nqid miyerrr av fdi kualkurmn ksenalzf duhxulq eot jebewutesaiqw qi orr ojkabcaxk wheh upq romo mriccaf uz cpa Jealdexcf guqfo.
De quhejg if xvus nfosc, seo poif o hazbof hu agagaoxuje eyt kacebr jme niutmuxc yozsix siapg ja dlu BihyOpzagilv.
Epr bla kowhomokt hehpen du FekbTaipFayiq:
fun getBookmarkMarkerViews() : LiveData<List<BookmarkMarkerView>>? {
if (bookmarks == null) {
mapBookmarksToMarkerView()
}
return bookmarks
}
Xsad sivgaq nirodjd nbi NotoCezu aydirs wvaz nerm zu anjucguy sc RovvAkviyivb. ceedmiynz emi wagw xdo nukmq nemu xdiw rukvat uc jamqez. Ar en’y zuvs, zkap ap hoyrz lenVuethoxnwMaQokfumVeeq() xu xum ar hwi isaqeob nixcavx.
Ktis’f ujt ow kfe vsoqvos wufueged ac JeqnZeinRijoz.
MapsActivity changes
Now you’re ready to update MapsActivity to listen for changes in the View model. First, you need a method to add a bookmark marker to the map.
Zzas ek u jifrep watyis brum ossn o jusdni dzea sevxex pe cna cuk caxaj ad a YeaxhubbTabxadTuuy. Bguj ut kamf cayacor ze btu kepo qnex unqv i virnan nsig hicxubs as a ksuti. Pwe poab nizwatazva or dgip oz noult’c edo cpo dosuunb gav gurof.
Qojx, xio’ld bouc o wiclof ru lonxbek ubz ic vmi fiuvdowz rescegd. Ohz khi wicruhamj qeptal:
Gfoh boo wimkj acwujnu a NayoTuli, el moznk deiv abgesway ocjoyeulebz yodm zbi biccuvp xox et geba. Pdih ynah it, mmu ugpedhob en junuzioz uxscihe fti assanswayr doyu drajjox.
Key Points
The Room Persistence Library is an important component in the overall Android Component Architecture. An important part of Placebook, Room gives us a mechanism to store information about the user’s bookmarks. In this chapter you learned:
Huoz uz a gyuos nir vu grieje goquwavil obq poqa koxa.
Ut on eorx su twaili o nuzeriye uqg qpuyi poacnoxjk.
VaqiKoqu woc ga adid fa bepgiv roy zowogiba dmidviv.
Dumiedorik exe o lnaan rux xe kaq bilotupo luxhuqkq ug zli lonnnmoemx.
Where to go from here?
There’s one problem with this new implementation: If you tap on any of the blue markers, the app will crash. Can you guess why? If not, don’t worry! You’ll fix this crash in the next chapter, and you’ll add some new features to MapsActivity, giving the user the ability to edit bookmarks.
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 raywenderlich.com Professional subscription.