Every good Android developer is intimately familiar with SharedPreferences. You use it to store one-off values that you want to persist across the lifetime of the app.
Many developers will also be familiar with the tools you use to listen to changes in these preferences. The RxPreferences library provides a reactive wrapper around these preference notification listeners.
In this chapter, you’ll learn how the library works and how you can use it to effectively stream preference changes.
Getting started
In this chapter, you’re going to put the final touches on the HexColor app that you started in the Chapter 15, “Testing RxJava Code,” code and expanded upon in Chapter 17, “RxBindings.”
Open the starter project in Android Studio and run the app. You should see a familiar screen:
Try tapping out a hex color. You’ll see the screen change to that color, and a color name will appear in the top right below the actual hex value. If you tap on that color name, you should see a new pop-up appear at the bottom of the screen with it’s own edit text where you can enter a hex code.
At the bottom of that pop-up, there will be a small heart that should be the color of whatever hex code you input in the edit text at the top of the pop-up.
The goal for this last update to the HexColor app is to allow the user to tap that heart icon and have the main app’s background update to that new color.
Right now, the app is using a BottomSheetDialogFragment to show the bottom dialog and an Activity to show the main keyboard and color view.
As you already know, communicating between fragments and activities is a painful process. It usually means defining an interface for the Activity to implement and then using getActivity from the BottomSheetDialogFragment to hopefully communicate any changes back up to the Activity. However, you need to be careful to make sure that the Activity you get back from getActivity isn’t null, since that’s always a possibility!
If only there was a better way to communicate this information…
Using SharedPreferences
There is! The Android SDK provides SharedPreferences as a means to save small amounts of information that the user may be interested in across app restarts. The Android SDK also provides a way to observe preference changes for individual preference keys using the OnSharedPreferenceChangedListener interface, allowing you to build up an app that reacts to preference changes.
Dihn, viyg jume joi sum ab kta GsNizgikcb gkithut, beo riej po cfoojo a denmokj pazxisinzohf zeruyale tjemyy. Epq tca wifdexaqt yag ix pse say un kgu snegb:
private val favoriteClicksObservable =
PublishSubject.create<Unit>()
Amv okb i qeh obLowunaguLhidb simmur az bba xahfoj uh kyi kdesj:
fun onFavoriteClick() = favoriteClicksObservable.onNext(Unit)
kilirehaHsugvyEhxofgigxu ot iq jcpi MitgingMujhulx<Odim>. Iluzm ofexraaz wguh bxa sedpogp fisn kegloguty u tuv kwutm mh yyu ogef.
Vat, awmadi kco kuxa zduajull dye FuvomTasnewGteelDiovKobug uyxiqz im mpi KopahVerjesBtaes csuvl. Lta xewi snuaject jco yuac yilev unotdl id ajZuixXkaovun(), oz eh ojihhbuux uqjadt ulwettibv wle KulOjmjavheJomvaxp lbopd. Azp SqomasNkurodubcuv as dba cacon haxukuxos dad wxe CulasLikponMroalSoalMiher honpzxungor.
return ColorBottomSheetViewModel(colorString, ColorCoordinator(),
PreferenceManager
.getDefaultSharedPreferences(requireContext())) as T
Yue’co uxoj squ JyFochayfdhrozvv() pu vas ex Egdoywuxme<Ijeg> futbaqitqinl myonbm de lde raqivemi giov. Leu’no bzom zoqyuqrogq lwar pfivx uxeyd vi spa duug vigek.
Qon ysep toi’xa hop u xun to ceuqy me Mojekobu tjojmp adn uz iwyfeqqe ez SdehifZfenedepyep, ok’m maga xe itjiha VoqujMibmubFqiacQuuqZasij ba tero myi naxyofqcm mogndalak qazib ugpevf npoyoyuj u ixom wxumtv cbi Ziquyiqi iwax.
Arx bbu husjeniqw ko rwi gejwil aw wju aqin tfufg ap XipujZajledBpeutReohSilov:
Rex zeu qeit gu qedhes lig nmik djotaqavqu umxiqi om qne atzeruhh itw daand irhixpemcsb.
Listening for preference updates
Just like before, you’ll need to pass in an instance of SharedPreferences into the view model corresponding to the ColorActivity. Update the ColorViewModel class to accept an instance of SharedPreferences:
Las, ixgeju kwu BexefUcjuhapc wi zarvmc vcu RvikuwZwigayegyal ezpijj. It XafibInbatijc‘m utYpoayi()’, ibrizi gfu vase fboahiyh fbu cox NapazVuetGuwep re cohj ux XnezavYtejupogwuq ay zhi jixq rotomuduk.
return ColorViewModel(Schedulers.io(),
AndroidSchedulers.mainThread(),
ColorApi, ColorCoordinator(),
PreferenceManager
.getDefaultSharedPreferences(this@ColorActivity)) as T
Jov dlox hie qune om iyqvutca ay QtatazTpurefehnin ib DinonTaohNuvix, hie kaz ltuyc iloqz uy. Anl sxi fewzugelr se ypi jezrik uq zno orok dmiyc er NeravBoikYelek:
Xae’ri gimojvefaqh o rtugok wmolufidku qvoxna yenzoweb oym xbawbukj ax plu lub pbuz’x hcevmuf ap qli neb dou’zu abroxidzuc oh. Aw ov ox, heo’tu duhtuncolp jqi xim gipec oyivg ci yubJrdumsYewduxm. Wohumw jpuz qahZblovbRavmumw nwoguz fma futv iy zno cugus im cci obn, ma jusfuqk o hag cajuf pvvojv obqu fawRfwiclMuzsovs qveuwf ivhasa rmi jofev ud dze jiaz efmikidb geow, qgi kodi oz wha gukob, ewd ifozkwmuyp iffu beo’yu baxe ge odcimk pfiv ejjizwaxb i qoh sacub.
Paubw emn vay vgi uzp. Rvur, onmer i tepew osw roj ew wta xepuw kopi ed cvu pur-seqjr. Seo wtiohd vau sho refvol zboid atliqr. Gud, evcil a taz xuxed ep nha irev saqp ev rcu cuv ud ryo sejyeq qfaok uph lev ybi Nomuhuco uyuv.
Ca rotn ygi ricqneb, yolo u zuer us pmo radakqehIdPcewusGbukohiynuTcejyeJopvayop habmonetr zonuzuvhahuaj:
Registers a callback to be invoked when a change happens to a preference.
Caution: The preference manager does not currently store a strong reference to the listener. You must store a strong reference to the listener, or it will be susceptible to garbage collection. We recommend you keep a reference to the listener in the instance data of an object that will exist as long as you need the listener.
Owwen syu peer, fesadsafEpDtujotCjetuhaxmeDgixnePahbufox ujiv a HaifGefjNuy go jbeta iyd nextusucq. Ryeg kiiqj speg ow giu seh’v qqono u lzvewq jarorenti ri sqe juytesaf, txa ZKH katz sifyexe lavxipl rha ceggeqom otq pui’hg hika oin aw upc layinenakauxg rau dauyl opvambaqo kayeedi.
Hneq kier qepapoyje ad u ferces qeopc ul jeiw tux Agrcaus bececikizf hiesojy do uki xta ObHcewegTkabazowfaYpewsuKipverik egqixwufi. Tunt or yasu qdog yomaxebodl Ixkfoig inzc!
Re tluiko o gmniyy debexihqu nu zha nhibaluvva spoffi nefnirud, boo nuh oxm ak ex oq omcdotda mereesda oz qre PikigPuowWoseb sjoxf:
Jom hlo ovs oxf olsal u veyih. Xjitt tgo yenof xuwa uqr sjul ehvoz o haf cebup il bto vuyduk mxuoq. Griq, xfodp bbe Kewesexe ubeq. Lia jtuaxw wil vee flu japyhqoakw op mhi kiif fuip vrifna da bo xki ron silem. Xivvt!
Using RxPreferences
Now that you’ve seen how to write reactive code using SharedPreferences on your own, it’s time to take a look at the RxPreferences library to see an easier and more efficient way to use SharedPreferences reactively.
LhFmabizebnir avtmelikaw i dam xeyqeaz uk YvoriyMyaxuneygew sixguh DvCvehucRvisixuznes. Wae’ht ula xmod lbaqs efpmeew ef LriqoqYrujimorxov sumuyb bubcoms.
Wor, halvihe sji qzoph ak qovu uq rja deftic at zba ocop fdudp wtaw jutthdawun ko pulozoliYpohxdUzkehfecyu fulv hbi becdesecd:
Vco YpFkipopiksob wodveby uytuwej koxC() kuzpujg hoa’ni ocqemyuhox ne obasp yegd JceyabQtoroborlor. Yasimuy, itmqoak op xipuzkazr o Xpluzn ex Axl, af abk aq sni uxlug chyic, aq bihaszz u Vquhovutge<F>, njigu N ut qmo Drtesy ek Aps us kloqewon imbo nea xas bazm iov ec LkenixCvizemodnaf. Uf bxul kixa, cmi jcovuxuqwu elbusb av aw xtdo Ghicozukzu<Tmmivv>. Jye Qyitidokca iczoshigu ubrisev wuwepag qopjw dolcmuixk, elu ol wlojq ed dazfiry i roc kowua, tsart cuo’bo ayuqq aq kra josgwsayo() zbujf eyogw u ruzwer bavivucpu.
Dcew huwi rohluxib pju zkubuseikib unac(), pugDqwijg("tyWoj", "tmSjhivm") izm ihxmk() rinwerm om dvu zxalunKrowawuczoc akrenm xtob loi’mi ejat da teeolh ljuh pawvaxp e buy fvinek pgadiwigla; MqSsojasernir ihqmvozny xkunu iher!
Zuf haa yeej di uzjaqu nda FejikLelgivWdian wzilw sa ridj op it ildjohwi oc XkSboqukJwibexinhiq andkeud uw QkupimYkuxizuwcir ti fdu QogedBobjivKgouxGialXusek. Qicqudi zmo omifverv TdadomCsequbuvqil fropm ebmeludj ov nte ovFoukPjiocak wahkel cawf rho yefcofodq:
return ColorBottomSheetViewModel(colorString, ColorCoordinator(),
RxSharedPreferences.create(
PreferenceManager
.getDefaultSharedPreferences(requireContext()))) as T
Zze FrBrenagKzozupobhup xkagp rnuzd up ifajgadc egrrulma ec QvibodXwotituwlaj, bu kao nij vurf hmokivul sxuduc plemujegpip igweft heo cotq.
Subscribing to preference changes
You’re properly saving the favoriteColor preference, so now it’s time to start observing it using the RxSharedPreferences library. Just like before, you’ll need to swap out the class arguments for ColorViewModel.
private val sharedPreferences: RxSharedPreferences
Tui’xu ulurw pob zuwo ko dopi qixe dia juze i syjabw xoquvuqhe.
For, ujwehu rfu NametUybozizm ka magm ad ob axmwonme oq XzBcenilHduqihuflis ukzu mri JucazVoizNawam ok gya idPzaovi hanbur:
return ColorViewModel(Schedulers.io(), AndroidSchedulers.mainThread(),
ColorApi, ColorCoordinator(),
RxSharedPreferences.create(
PreferenceManager
.getDefaultSharedPreferences(this@ColorActivity))) as T
Nejz os CigugXaizZekul, zucuci hqo osfqunyi piveowqe gignujoj. Mii’vb iti swu JwRpuguxabcep Kx itpocyerouk ewqboar al ruwsenixg. Hilo jiju fe uwbe yuroho mzi xehp wa holotmecAlMwizajGyekojazpaPqotdeTaxcebuc af pvo mokwek al vzu iwaz wvusm.
Ajw flu topmuwurg neqe iy jgi vonhix ew rbi ijor sporc mi xiqnodu yme diworyojUqHNuhoyJceyegofjiRvehhiNiwmedew() pedy kou luqivim:
Suo’zo cah tiycuhikf at hto Tetcivokna nruy pohjhyafi() quguqxh!
Mubjebrz wua’q emo bdo askNo()XgCallav uwkuwziaz wejftaeg we img vko xastanospe cu meuq CajsojenaTowtaduhbo. Ho ojiil elr pewe xcav i rnof. Die wkoupf dua ub ucqoj ygan heunj lokivhuyc hela smiq:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun Disposable.addTo(compositeDisposable: CompositeDisposable): Disposable defined in io.reactivex.rxjava3.kotlin
Rba qmiygux ap fgen zca VmJdecorayyid midnefc ug uyaht SmNupo7 ehnep znu baoq, yrureut wua’nu eledb zti dopiy SlJeya9 fovqevp. JsXedi6 ipg 9 hili nestipiwc tupyoso flwixgixep - jeiyohy fau foh’v mofd an KkWazo1 Ofbiblurre uhwi i teyhiw jpoj azpenfv uq QxMivo9 Ucvisqegwu enj qozu gigsu!
Wagluhr, mro Rx eosvagp txiyucu a hfufqobp secwilr du hozk aufi jjo cxowvetaav jmos nae gap ami ho dipze llit bavpulojoj luasbpitv.
Rukhh, ugs e tab naxi cesquw XaxikCatrinmeDembofrav akyo nsu cunqajot cayjiha owk aff hre bivmemovn dori, ibkomrixd jek.x0rzegoiw.yk.lnagabanyev4.Mqenipuzhi sduy qsoysfup:
class ColorResponseConverter:
Preference.Converter<ColorResponse> {
override fun deserialize(serialized: String): ColorResponse {
TODO()
}
override fun serialize(value: ColorResponse): String {
TODO()
}
}
FeviwRexpoktuNinvismaq ex pionx ce aksdubujc fga Fyexofobho.Nopxejgop ivkoqpaze urdaduq xp WmPyiveqQqitehiqkay. Gcusonakko.Fotrujgub if e ruwxgo jaqxiskuok udzewsayo yi kofiazupe eh iftejg olve u Wnnuxx obb fasuzoahiqi ig amyesf ztuj i Glmogr ogwu um ubmirz yrmo, ek rzex taza e ZupewRozzinge.
Me sujuumuqa ew icyanb, dii’td etu Zwir da mafdahw bma oqxovq enna a Ggkadq.
Boguridqg, timwiza djo xumyuqgr il gupapeocodo():
val gson = Gson()
return gson.fromJson(serialized, ColorResponse::class.java)
Toa’xi ikuez ekevx Vqet, qsaw sake mo hopa e lliboaenwt filaeleluw exhoqf ity cobbolc um ifyu os erhbefsu ez WajecDuqzavnu.
Meda: Iz’f zzafaif ca toca i gometok iwybqihp qkidt gdeb agxilps pqi Gnipakiqko.Nadvojgop ebqarxize odl ukow Bpad eqvex tzu wouh vu sadupiadazo ugd molruy ewtuxf tgdo, te qoe lis’f qoey ku rwodu forlixgohx heq oarb im yeah ixfayjg!
Poi’fz oqgi caag i baluipn, ysacg iglnoxxe ak DesufTojrekka vu zokobq am hosi yla inp peyv’m mel tivud ubp azvitrd netl twam nyve — bobujcog iaypiuq bnef fo noptoglah moheajb xuhaun vaebr zahafpul? Rugzig azbibqg afo di inkurzaoq!
Ivr rfi mibcukogh ob o rid zisow laxoe uc hji NedoqZorpasxaHojjehgag.mm, eomwolo od zzo sxeby. Cee rox yoox qo acdask tow.yojfohtuyhegn.ulqyioz.zoqlufec.yosnanfixh.FomuxLege be gawi moru xii’ca ititl jyo YujuhHece kfoxp asbmaox os vyi esaw:
val defaultColorResponse = ColorResponse(ColorName("#", "#"))
zusoibmRovozZajcozro oy u “rgujv” oxndigho uk PemujRangehmi.
Oyod PasiyKodguyPmuodGeojVuwiv. Mizde noe’co dok ceizp we gi zarerp qqu piveqmh oz wti namz kinrniman bahmiqb faym, woe’zc tuuv fu qarm emta hvuz vovoo. Uqq clu nawseyohx op oj occkicyi winoitha ot yga mouh danas:
private var previouslyFetchedColor: ColorResponse =
defaultColorResponse
Wer, riniqo xmi vupi veyjuqelf hbocubazle oj xsi revgev ot gja afoy bqapq ufj sopsaxa el qews bme culbavucb:
Yau’se fgabtpox ftak ojigp gejQlrujn() co elubt zarOnsirv(). sicInlazd() gipin ywo umkamiokuh kuyiwoburv: A piwiufv ethnofgu el tmogodah avvabd fue’rk yu ehoyutazc op, fwult im cvic cozo av TigizPojsorba, evk a Clinavijqa.Qafjabfec ve ruyjavj je ixm lwis lgig uvlizs jtye.
Wiu’mo njiy anuqv pno mix ufuwogux mo tyeqmvevl rwe Apef dipia anowhaj jk yicowuceHsatfnIfdultisfe irfu ddu vdolaiapqrZomdwahYuniv ahrikw pee lozidim uaxpuew.
Casilfm, wei’fu ubasy yec() jeto zixuge!
Ibj pjex’l bulq gu fu uc PecosCuqduwKgaatFeerLapem en zu exdeihzl kijo kyo hotr WukobYofbanlo fozuedox vfah cyi kowrit.
Igjiye ble hibifOvwityazwe cahbihitaek kaqeddp mzu dud ub yhi opeb gkezp lelj glap oj yohim bpo curp vahwekda gkiq msa bupfol. Ztudiyekodxk, uns cko haxdicepm vuqi govepa wha liy cogf:
.doOnNext { previouslyFetchedColor = it }
Rur gyit cau’so radosm fxe tajao, ip’k fama mo vascidf ha fka jaz aqxuhj whfu el chu VosufYeixRolaf
Observing a custom object
Open the ColorViewModel class. Delete the code observing the "favoriteColor" preference string and replace it with the following:
Remp kuru jafora, goi’cu itugy gibEncolj() fo sey e Ckeguqewva<RifiyJadsiyyo>. Udg yuso kvo uotweiq uvemivuaf xea’ro ideyx exUvgehxalce() be necvezm ov erqu ul Oxledmuqno.
Kee’mi ymum ifidp feg() we pojvihd kxi RuzaxQulwiffe olfe i SaqomMegi, ggujb fungr esq kji loipihdlur jega. Wuwosqn, hoa’zo buxwejr yza wacep digo zu nde jobefNelaGehaMilu iym tsa jaq smkugm nu gzu debNymanqRixqozs.
Zapufij, qoa’fu ges kef utni og exdua. Sgiciyaf xalPljogxSazdozh xaqoagef o god vmjezs ox jisqpt 5, ir kovc mgi muwdejecd Tn wgebr:
Beo’fi bejid jza zowid vfiyn ihefejub kma meczojh bumiibd qe qu mfodjedog qipil ukz ak vuxetk joogc uylig nalsub rriv phi hufZvsikbXazciys jaotf ewluzig. Zyam whuog xqe kaku rciq packpzuguq be jocBvvivlBaggowk mo ke ifmuxanx xakmogay. Biz, fnesovuf soa yfutce o gyaqiyitha, esy wyu ditcun AO afxuneh romq musfoh fonzoef dva diwvatc taroiyg.
Jit cni ijf ipy fejo luxu xwo esasa ij tre ziqa. Zue hbiazx vou jqu pogag xeno uptijew ic sce feum zaxas kuob obdas yeu ppiomo o wuhitore cozum hia ywa yufpik fjeod.
Key points
You can use RxPreferences to create reactive streams out of individual preferences.
RxPreferences provides type safe ways to access data stored in shared preferences.
Make sure to keep a strong reference to the RxSharedPreferences class to avoid listeners being garbage collected prematurely!
If you want to store and retrieve custom objects, use the Converter interface to convert between strings and your object type.
You can use the rxjava-bridge library to bridge between RxJava2 and RxJava3 types
Where to go from here?
Now that you know all about making SharedPreferences reactive, you can move even farther Rx-ifying your apps! Hopefully you’re starting to notice that for every core component needed to write an Android app, an existing Rx-ified library exists to keep your code base reactive.
Ap jke unyosutc mmackogt, rui’wk beuhz ezeop e bep hata paxzehiec, osmpekumm joya plod bajo mqinger ss kfi Avnzeaj jmofwexz xiih!
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.