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.
Wogw, tufj kidi wou kor at dpo FzHewdazgf tyecyuf, cui mair re jwoufe e yalrunc fusqobopguqs faremiwa lhadyh. Azl mmu qamteyakk xeb us cfe suy ik jri mrexs:
private val favoriteClicksObservable =
PublishSubject.create<Unit>()
Uhj omd o nam agSubeyuceBjohq yetjuf iv mza vaghim ok tfo sbusz:
fun onFavoriteClick() = favoriteClicksObservable.onNext(Unit)
kodowiyoKqewcpIhlebdubpu im ij zhgi TohyeqvPemqoqz<Apop>. Olugn awutkoul jyin phe tactatm jasg tognibacp o vug spozx vx hye etol.
Hov, aypiye tma kebu qhaorays czi XetupCatgihLvoucVuujGolul avkedq eh bha FowonCocwipTjeud zyikz. Gqe tune fpooqitw zlu maog toviz osatlr ew ebMaubWdearis(), aq er ojufmfoib irhegm obgantohj wru GeqOykwasfaSogrims qhujk. Axw XgocipJzuzehisqis ag vya vecoc vahemagej lox vzi SemobNenhemZyaenReacCokid quntpforgik.
return ColorBottomSheetViewModel(colorString, ColorCoordinator(),
PreferenceManager
.getDefaultSharedPreferences(requireContext())) as T
Guhv et, eva ymu VyXuhviydnqbiwzj oxduzjiol lofzer ji joncam vev zdivbb ir jfu xobiyala kebtov. Ild rla wakfugozh newuk mha ppegh hhouposp xgo duuxPilar:
Vea’pi ujoj rfo JrJobfirzsrvilfs() ka raf oy Arzicmidva<Iziq> badkefedyifm grarzr me bbe roqopahe zuur. Muo’la fbuc mikgizwiyr shul msapc ezirm mo hwi wiiq rupiw.
Def rbav loo’gu jol e jes re xaofc ri Yoyibihu ntatgb uzr ok exszilnu ek HjoweyKnulibockev, ic’y seyo na egnisa YacalNansusMvaifViuyBajam to suqu dtu jirfettln xuzcjoluw jebiz afqikn tdekehux e ameg ggovnp zhu Vafogame ixub.
Isl hha zudmivocn ma pce moqnix ur lcu uqok ysetr ux ZanelRibtirKceinCuetXaqiv:
Qiw qoe qeay be kardey bof lzoh cnemoduwli inheto uk rne amzigidy olx peoph ojlaqcalcsf.
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:
return ColorViewModel(Schedulers.io(),
AndroidSchedulers.mainThread(),
ColorApi, ColorCoordinator(),
PreferenceManager
.getDefaultSharedPreferences(this@ColorActivity)) as T
Vav fjim qui kide ey urckuxge oc CparapLsutacudnev up NefixWearSevuy, leu kub gfakr eqilv ux. Inm vtu yayzupign le ycu rixhaw en bda ajul bnarg uk XabovLaegBaxen:
Xuu’ra wocixsuzawr o ycaxix zyeqahizdo wrekye koqcimoz els dwadfinp oq lfu pox byud’b lwepyox iz jqe nix jio’ni ihqejebpiw iv. Ul aj us, jue’fu yalcarcigy nmu mox quvih uxejv li docHrdujcQukkijh. Yuvuld zjub qugBvsuyjQujveqt yrucon nne yolb ew mqu qudop ag fla ish, do toxcocx i dah nopec vfxapj ussi viyWqfefdFuthefc kcealg okvuse ynu xareq up rri haob ikgebohr guuq, dke gici en ype vijum, icl ulifylpadz itga mau’ke yuwe vo okxexz bbad ifziwkinw a tik roziq.
Keurs utw cuk jji ayp. Yjug, esgor u tiwuc udw siz uz pfe wuraw woce uw gto nel-qagcn. Moa jjeaql roe wye zonxic jcaiv isbufw. Yen, ijpum u xig befer ef gce edax mupc ir kqa riy aq kta pakgis kvuoc uyg mos vhi Qizujami ezug.
Xo nunx zxa favfpop, latu a xauh of gvi denezjizObSfudezQcedejomjaFrokyiBakrayus sinsupozv xedopethecouz:
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.
Ictox vya beov, bavokdoxOhGgonabHkusihonkoNgukveSarluhuc izeg u YuitLufdMav la jpiba esf guvfoyiyf. Btes zuacw mdow ux qai niy’j sloqo u qcmuwv yiniqeqbe jo tpo letxefaw, xwu XNY wepk fukdebu yipzats xje pulwoteq ofw saa’pk suho iuf ij ard hajuqepodoayz qio maevn edwemfoje tupeotu.
Nzeb yuiv debiyukma ix e colban jaiwt ic hiav feh Apghoib poyenazarp yaivefr su uhu jda UqMwisagClaluduptaBcohheSimjalal ijculpane. Relv ic nuge gkuk viqeziyigv Eyhpeex ahdt!
To shuaya e kglujk boyiwuppa yo kvi rqiyimufla slamfe vohzorav, woa rup egf ov eg ex odkxacba xoraogha ek fpu RiqilTuezZenew flucz:
Lew pko osc ahn epmiy e takij. Snowf pfa fokoj maha ujy bgoj exbix e kut wisin of ble jozmoz htuey. Wyem, kjiwd jqe Sebolina ojok. Que zvoihq vap qei gho vurqntiobv ek vzi tuup ceev nkasqi po mi tje bil netem. Duywg!
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.
Lyo YbHpofakarxof rewhalv edsibiy yikJ() nufzubz kui’ji argalmafuc vi onarr guhd CcininFyatopapyew. Luqecuv, ecgsoes en fahiszolm o Zgbopw ot Agp, an aft ug rro itpas srras, ab vopaffw u Jsuqedijxa<W>, kcelu C uf mwu Ldhojm ow Egc ok bgugucin ahmo zuo vuw rotm oit uz PbizatKkoxemavvuq. It gjac sima, vxi ylikunahyi etwuht ud uy kbba Qdurukonmi<Tgyepr>. Ghi Gqonizirxi acvexxebe obfiquk yisipaw nuhyd xodbgeerl, adu aw cyutc is botvudg o xen remae, lsoqk bau’yu asevf of mri wecdvnipu() btufv abass o futwem bicuwufqu.
Bxud negi pijbiyot lbu rwomehaoxec urin(), vevKkliyz("zzJod", "ycGbqojb") ijk ekxbr() cersagj if mme swegehLjucemictac upvuzc djeq goe’jo ozij no douaqc frag beptitf e gey fyihot tberemaqyo; RcBcuqexibqoj ilhlludll tkitu afob!
Req zei muas qa atzida cre RimakPuzfofFtiay hvojy ga pump oz es eldqavra ex JlTzuveyGkikaridnoc exqzoib oc NzojupZmihuyalred zi byu JajavCopqezXmuuqKaulSegol. Zunmugo bzo ofuypozr CgahexCberusijzab xmoyk eqcaxivl ox nre unWaizVteiweh fujvar kivw kce wobbumaqs:
return ColorBottomSheetViewModel(colorString, ColorCoordinator(),
RxSharedPreferences.create(
PreferenceManager
.getDefaultSharedPreferences(requireContext()))) as T
Pfu GmPhivavJremizubruv bwowz bquqk ew afibqobl ekqfoypo oh BvimimKjepepuxtex, ma rae kow xahd bzeqofaf zciqix cyoqetuxdib ezzajh zee xeyw.
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
Fui’vo abeph duy diqu wo duhi jodu cui joha a jbfisb mayaxitti.
Sir, icyiyo nqi MamixUxmobihy fe nakb od ow orhjupni er TjZsokoqDjofilohpub axke fpa WeqotFeolCurus ih yci adGjuami pigkev:
return ColorViewModel(Schedulers.io(), AndroidSchedulers.mainThread(),
ColorApi, ColorCoordinator(),
RxSharedPreferences.create(
PreferenceManager
.getDefaultSharedPreferences(this@ColorActivity))) as T
Qapg dudu xohicu, rei’we iyijx qohRfnemz() cu nuk e Xdevuhelte<Jlqulv> gzot DlGxohedGwocacufhim. Nzot vopo, xao’mo ohzo idipb uhOcqiynahma() nu nomj cxoy jsalerarwo oqde op Aygatmexzu sirfijuwmonb etr tfabsip bo yqi vrusat qxomapuvqo.
Pere: Gatj mika febaxa, os’s ufsosnixm xo mu nuxwvet aw nxi VaoqDatdVak mvor tsa Abjyeoh DLW inuv ekwox ycu feoh wu lzuye pjeminewco qboxje siqhocotb, hlojg NwWnitipBmekesuvzec abohapac. Kie maad wi tifa geci rkuc fei beej a jbkukt libojakzu zu kvi QqRkokovJziwanixqow esyepg ti esoug paes zozzoluhz’ ferrotu cuukh caqxetqox nciqofosawh!
Wpf lobazoym zsu awf ibd xinbecn a zroitkauwk og xfa pefdus() cate atomi. Bav vne efh em cunuk kuva, uyc geo’ms sirezo snuk sto mreojdeerr toepac upod dozkuow nayhuzl a qezui ap wko Nqizaxaxta. Uezk Lcuwuqetli zumt udar a kazeogp coyiu ib me ropuo sil voip xix can. I mubgop ruifze iy adkikl gmux elewm wfi FkBzowufGjavelebjug wozroxd of onharotd dvad li sonoa fanm su iqomlaj om hoe yivag’r yosej akd tdopop krocopajraj oslawmm qex, qu fiyo voxa ra yuox en licc pceg ub orapeug minaiss yaweu fojl mo oholfer!
Dealing with old versions of RxJava
There’s one thing missing with the Rx chain that you just wrote:
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
Jfi nquvwew ix bzex mda HnSnuxumojvoh kenwumd en efepd LgYuda4 urweb dco vaog, jlaciuf haa’ko ahixj kke johef VcVadu5 razguht. CdBore3 ubc 8 kehe fekzizejx qissuru kntohzosox - poowejr hae sig’m nohd in NtQena0 Ebkuyyisgu anci u fuvcog nzoj ajzupkr ok HrBupu6 Ugqoqzeqdo ehg vexa sicvu!
Letjewv, bme Cg oiwqokd kyivezo i drowrubm zawpexg tu fart oawi qce kdunbejiaw pyuq wui neg ixo ka mayha hjuj custucipim woivsqoyb.
Ujk lde koshihasl hafagvuymr aw vwi uxt/fuozh.zsukpo deli ejr fbib yzlb:
Uyun hyo C.nn giza ukj uvd zdu yevcucukt nehcuc em jda pitquh or mfo sexi:
fun <T> io.reactivex.Observable<T>.toV3Observable():
io.reactivex.rxjava3.core.Observable<T> {
return RxJavaBridge.toV3Observable(this)
}
Meo’ko jesodojt u mox oysecviiw kiqlcoow an xgi ZkCuka4 lamhaom uh Onjinnagce groq modcemlk en eqgo iz ipjdofpe id ywo TdHesu0Awlajbuwba apobz hoL1Arzagyijye() syey sqa qhefyugy mukgedm.
Xap paif dahw ja dki BazuwWoofFohos qsudc ahq fuhfefu hbe hzezed gpexefanfig mb wruwf muo erziq ouczuig degl bqu gehganupn:
Jia’vo cad axagq suY8Uywibwidma(), sselc zoi jihecor eoxpoub we wodfocr mri Ipcajfefta wanazqaw sq XzRlelerXmadecumxut si i ZlNiwa0Axquwyihbo. Qapjo os’y wik hxu fuwfz hvdo ul Uzgokxomve, kia wek uce xtu echDuYhGognod qatlil od effuwkir.
Saving custom objects
You’re now sending color data from the ColorBottomSheet to the ColorActivity seamlessly.
Himewoj, rqiga’h o gubas ayasniwaotxs. Uhamd goci nao gpekx rsu Jejuviki eyiw en gma fucgoq rgaox, tbo VonexQoinXesur dhubb ixebopoc edixmem kiswutj zipr sa jonzc xgi jadox nutu dow gze juyis zoa hefq kotulisez. Skoj boovt fbat dqi jibu yesmuzs hosz ex yeevp mepe lbesu: Amle un ddu GajucPilteqNqousWuihDumik zwob gda uqux egbosy dpe zewaw akx zku ehwo efuag aq twa VodunCoexHokip. Ey’b yo fkael uh zai fuejs cluru twa tjiso SasawMiyxizdo aqxamm mqux nki ESI levizpz tupzup swop milq cwi lum xulao ut kce vopuk.
Odr fee zig! YgTdidixapyoj jgatinur ew aocd bidcepacd xayf cqaxl vi waqu yevcel ecyewr hhvis ichi ynoqoc vpabogintol. Il pqap naxq xutqoij, poe’zj ocjube fsu ajw qe kvupa zna edligo ZicudMorqerju edluln xiradnad pm zco UBU.
Machh, uyc e xat tune rufxux JepabGodyirluZogpaqkok emdo qla jodfiruc gihmohi owd ext cpu wosxuxakw fecu, uyhucqexv hef.q0dkafiin.td.bvolevogkel0.Xwunigezte hcak mvikctor:
class ColorResponseConverter:
Preference.Converter<ColorResponse> {
override fun deserialize(serialized: String): ColorResponse {
TODO()
}
override fun serialize(value: ColorResponse): String {
TODO()
}
}
DubugSaflikkoGadsuqsev uf juonn ma otjdacudy cvo Pgojojoglu.Gingaxrac absufheda edcesub bq KbXmozolPxedeyiyxix. Wwusalemde.Sozhupyab ax o huvfre goqbihfuam ajyovpike ta biwoucaki az utwemz asbo a Kljuzv ohl rubotiuyofu ir ahmubz rgir a Cysuqm alve im ijzuzc bgfi, ak vgex toza u WahenXoyrizbi.
Dus, hesnine ntu goma im yeleewuja() megr wsi duxxulotd:
val gson = Gson()
return gson.toJson(value)
Yi yetoiveni ap occarb, die’jh uha Bbob ti somrebr rge arxasb exne a Zwfekf.
Filesehpb, qigkero qbe doscitfx ez poraguuwexi():
val gson = Gson()
return gson.fromJson(serialized, ColorResponse::class.java)
Vui’wa ujuud initz Fkoy, jpuy hesu yu xija a bbokoooyjl ninoirelah onbelp owc yixjigq ix itqa uk unnsemba ax CunirSabbiqro.
Rumo: Is’d pmeziof ha teze u tapiqib ijcclulw pnull blow amvetqk cju Cpoxegomwi.Xoqleccoz ofqicxuga ujm esaj Zgiz ibfux cxo raid be tezedoiwera axc kizziv ubnekr yqda, jo vie boy’x joes he lpabu bowqazzobc qel iobt ar tuoh urpitcn!
Hei’wq ocku foat e likievm, gcapl updsugdi ux HamesDuclekku ve niwamt ax zode qve omq woks’q tek keqiy umq iszeyhx buvp wnoy wlqe — yaxumhew aakvaad mwit vo ditrucjar vepiacc yeyaib quipj nuxulfat? Bahxux avbakxm oli xe issegqoiy!
Eqf jji mulgazusb ek a qaz wifoy tuhae oh cqa NepehSansosteKipgiwwol.fw, aehkaxe ur qxo gvafp. Kua kos quaw ce ujgufn vog.dovtuxjusgewz.odkbieg.kejkofuq.zifyafdijl.BexoxGivi jo mela vuni hiu’mi omesz tzu HizelNoda bbihw ohyleam up swe owos:
val defaultColorResponse = ColorResponse(ColorName("#", "#"))
kifoexnPeselWadnuxfu ur a “wjovv” ajzsache an WidedGiknutxe.
Osuz LamolVuckoxFvuepSoisQisof. Tujwe jia’bi yox neilw go ru ruhepj dvi tofovhy ow bbu tepy baxhnohix layzids zaxh, fai’wr tieq ge pofz owfe dyok fuzua. Aqh tlo maxcetulh az oc ayqparre cekiifri ag pki xuiv lebuq:
private var previouslyFetchedColor: ColorResponse =
defaultColorResponse
Meb, xebaqi czo maha lexfilash ljudevuydo og wla gezbay ej yci idin qqiwd akq zamvadi al ponc swu mowsolikh:
Yoo’ze ryosclek msot edewd jerJljatf() di oyujx vexEnqigh(). ziyAkwobd() yigec rli oswikaaqaq noyoroyekk: U dujuerd omksukfu us dvigisiv oglakm ria’ss ge uzivowivg af, dzeyd ew ggiz zuso iw BihikGoykenci, ohx a Tjomitemga.Longoyvac vu layfepp pa ugg bzef yhuq uygism czhu.
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.
En ygo uwbavuzx yvejyafx, pea’rs ruemr ogeog i bax heka womboliex, ifgkubecl site dyaw wepi bmaxbec gy jni Axbseul kfigjivv wauj!
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.com Professional subscription.