MVVM pattern

MVVM (Model-View-ViewModel) je patern koji razdvaja aplikaciju na više komponenti tako da svaka komponenta ima svoje specifične odgovornosti. Pri korišćenju MVVM paterna “kod” aplikacije je razdvojen na tri dela: View, ViewModel i Model, ova arhitektura je preporučena od strane Google-a kao jedan od najboljih načina strukture koda Android aplikacija. Osnovne karakteristike ovog pristupa su:

  • Komponente korisničkog interfejsa UI se drže podalje od poslovne logike
  • Poslovna logika se drži podalje od operacija vezanih za bazu podataka
  • Manje briga sa “lifecycle” događajima (jer se koriste komponente koje su svesne životnog ciklusa drugih komponenti aplikacije)

MVVM diagram

Preporučeni način za komunikaciju izmedju dva susedna sloja je tzv. “Observer patern” (najčešće koristeći “LiveData” ili neku drugu biblioteku).

observe_patern

LiveData

LiveData je tzv. observable data holder zadužen da čuva tj. ima referencu na niži nivo o podacima i da obavesti sve zainteresovane posmatrače ako dodje do promena. Najvažnija karakteristika LiveData je ta što je svestan životnog ciklusa drugih komponenti aplikacije (aktivnosti, fragmenti…). Upravo zbog te karakteristike LiveData ažurira samo observers (posmatrače) koji su u aktivnom stanju (ako je životni ciklus u STARTED ili RESUMED stanju). LiveData samo obaveštava aktivne posmatrače o ažuriranjima, dok neaktivni (destroyed) posmatrači iako registrovani nisu obavešteni o promenama. Kada koristimo LiveData ne treba brinemo kada se završava (destroy) životni ciklus aktivnosti/fragmenta jer se automatski odjavjljuju čim komponenta završi svoj životni ciklus. Više o LiveData pročitajte u članku: “LiveData & MVVM”

Kao što se vidi na slici ispod, jedan sloj ima referencu samo na sloj ispod njega, ali ne i obratno (tj. sloj nema pojama o komoponenti iznad), pa tako: “View” zavisi od “ViewModel”-a, a “ViewModel” zavisi od “Model-a”

mvvm diagram

MVVM je sličan MVP pattern-u, stom razlikom što kod “MVP” pattern-a “Presenter” ima referencu na “View” i direktno “govori” View-u koje podatke i promene da prikaže, dok “ViewModel” ne drži referencu na View i ne može da direktno utiče na “View”.

“View”

“View” sekcija (sadrži klase: Aktivnosti i Fragmente) je zadužena za prikaz interfejsa i prihvatanje akcija korisnika. Pošto ova sekcija ima referencu na nivo ispod (ViewModel) ona može da osluškuje promene u ViewModelu, i ako ima promena da pozove neku metodu iz ViewModel-a i preuzme te nove podatke. Zbog ove odgovornosti je potrebno da u okviru View-a postoji deo koda sa kojim se “View” prijavljuje da posmatra dogadjaje koje emituje “ViewModel” (streams of events). Pročitajte više o tome u članku: “LiveData & MVVM”

“Keep the logic in Activities and Fragments to a minimum”

Nakon preuzimanja novih podataka “View” ima obavezu da ažurira prikaz koji vidi krajnji korisnik. Još jedna bitna odlika vezana za “View” kada se koristi MVVM arhitektura je da aktivnosti ili fragmenti više ne moraju imati odgovornost za čuvanje stanja jer je tu obavezu preuzeo “ViewModel”

Napomena:
Conditional statements i loops ne treba da se nalaze u aktivnostima ili fragmentima taj deo treba da se nalazi u ViewModelima ili drugim slojevima aplikacije.

“ViewModel”

“ViewModel” takodje ima dve uloge:

  1. Pošto ima referencu na nivo ispod (Model klase), ViewModel može da osluškuje “vesti” o promenama koje emituje Model.
  2. Kada dodje do promena podataka da (nakon obrade podataka) dalje emituje vesti o tome (ViewModel ne interesuje ko će te informaciji o promenama iskoristi, njemu je samo važno da obaveštava o tome).

    NAPOMENA: “ViewModel” ne drži referencu na View te stoga ne može direktno da utiče na “View”

“Instead of pushing data to the UI, let the UI observe changes to it.”

“ViewModel” je klasa koja je dizajnirana da preživi konfiguracione promene (npr. rotacija ekrana) i sačuva informacije koje su neophodne za View (a to znači da naša aktivnost/fragment više ne moraju imati tu odgovornost). Kada se “View” (tj. fragmentom/aktivnosti) uništi promenom konfiguracije ili rotacije uredjaja, njegov “ViewModel” neće biti uništen a nova instanca View-a će se ponovo povezati na isti “ViewModel”.

NAPOMENA:
Iako ViewModel može da preživi promenu konfiguracije (npr. rotacija ekrana), ipak ne živi beskonačno!
ViewModel ne može da preživi ubijanje aktivnosti od strane opertativnog sistema ili korisnika (npr. “back” dugme). Ako android uništi aplikaciju/aktivnost to će uništiti i ViewModel, a onda samo onSavedInstance() ili baza podataka pružaju mehanizam za čuvanje podataka. Što vodi do zaključka da ViewModel klasa nije zamena za “trajno čuvanje podataka” ili čuvanje podataka koristeći onSaveInstanceState()!

“Avoid references to Views in ViewModels.”

“ViewModel” se takodje često koristi kao komunikacioni sloj između fragmenata u okviru jedne aktivnosti. Svaki Fragment ima pristup “ViewModel-u” preko svoje Aktivnosti što omogućava komunikaciju između Fragmenta bez direktnog medjusobnog kontakta. Oba fragmenta mogu pristupiti VievModel-u putem njihove aktivnosti u kojoj se nalaze. Prvi fragment može ažurirati neke podatke pozivajući metodu iz ViewModel-a, nakon čega će ViewModel te promene emitovati (pomoću LiveData), a ukoliko drugi fragment “posmatra” LiveData on će to opaziti, i na taj način dobiti informaciju iz prvog fragmenta.
Više o ovome u članku: “Komunikacija izmedju fragmenata koristeći ViewModel i LiveData”

NAPOMENA:
Preporuka je da “ViewModel” klase ne koriste android framework klase tj. da nema android.* imports u okviru klase.

“Don’t let ViewModels know about Android framework classes”

Prethodna izjava da “ViewModel ne treba da ima referencu na View” je iz razloga što to može izazvati probleme u slučaju da se View instanca uništi (zbog rotacije…) u trenuku kada ViewModel ima i dalje referencu na nju (npr. kada “ViewModel” traži online podatke sa mreže). Upravo zbog ove preporuke a da bi omogućili View-u pristup podacima se koristi Observer patern. ViewModel emituje dogadjaje vezane za podatke (najčešće koristeći LiveData biblioteku), a View se prijavi da posmatra te promene.

“Instead of pushing data to the UI, let the UI observe changes to it.”

Kada se kreira ViewModel klasa potrebno je da ekstendujemo ili “ViewModel” ili “AndroidViewModel” klasu (koristite “AndroidViewModel” ako vam treba i application context u okviru ViewModel-a).

“Model”

“Model” sekcija sadrži klase zadužene za direktan pristup podacima sa ciljem da abstrahuje i pojednostavi pristup tim podacima. Pošto često podaci mogu dolaziti iz više izvora (baza, webservice…), iz tog razloga je korisno da iz ViewModel-a imamo samo jednu ulaznu tačku ka izvorima podataka, a ta tačka se zove “Repository” čija je uloga da apstrahuje više izvora podataka u jedan API. Repository nije neka specijalna arhitektonska komponenta već obična klasa koja ima pristup svim izvorima podataka. ViewModel praktično ima direktan pristup samo do repository preko tog API-ja i na jednostavan nači kad god mu zatreba može da dobija podatke (ne znajući iz kog izvora stvarno potiču ti podaci: web ili baza podataka i na koji način je došlo do njih).

“Data repository as the single-point entry to your data”

Repository diagram

Podelite:

Ostavite komentar