MVVM pattern

MVVM (Model-View-ViewModel) je patern koji razdvaja aplikaciju na više komponenti tako da svaka komponenta ima svoje specifične odgovornosti. MVVM 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

Pri korišćenju MVVM paterna “kod” aplikacije je razdvojen na tri dela:

  1. “View” — Ova sekcija sadrži klase (Aktivnosti i Fragmenti) koje su zadužene za prikaz interfejsa i prihvatanje akcija korisnik (nakon čega o tome obaveštava “ViewModel”)
  2. “ViewModel” — Ova sekcija sadrži klase koje su zadužene za pristup podacima (Repository) i da obaveste “View” (aktivnost/fragment) ukoliko dolazi do promena.
  3. “Model” (DataModel) — Ova sekcija sadrži klase zadužene za pristup raznim vrstama podataka (baza, webservice…) i da abstrahuje takve izvore podataka kroz jedan API.

mvvm diagram

Svaka komponenta zavisi samo od komponenti na jednom nivou ispod nje, “View” zavisi samo od “ViewModel”-a, “ViewModel” zavisi samo od “Repository”, dok “Repository” jedini može da zavisi od više drugih klasa.

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

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”.
“ViewModel” deli informacije sa ostatkom aplikacije tako što emituje tokove događaja (streams of events). “ViewModel” nije briga ko koristi podatke koje on emituje i tako da nema potrebu za referencu na View.

Observe pattern

Preporučeni način za komunikaciju izmedju “ViewModel”-a i View-a 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 informacije 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.

“View”

“View” sekcija je zadužena za prikazivanje podataka i prihvatanje korisničkih akcije koje dalje prosledjuje u “ViewModel” (tzv. Pasivni prikaz).

Keep the logic in Activities and Fragments to a minimum

Pored navedenih obaveza (prikaz i hendlovanje korisničkih akcija) View je takodje zadužen za ažuriranje prikazanih podataka. 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).

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” je klasa koja je dizajnirana da preživi konfiguracione promene i odgovorna za upravljanje podacima i pripremu istih za aktivnost ili fragment. To znači da naša aktivnost ili fragment više ne moraju imati odgovornost čuvanja stanja podataka pošto “ViewModel” preuzima tu odgovornost. “ViewModel” takođe može da upravlja komunikacijom izmedju aktivnosti/fragmenta i ostatka aplikacije (npr. poziva klase koje sadrže poslovnu logiku). Takodje se č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.

Don’t let ViewModels know about Android framework classes

Preporuka je da “ViewModel” klase ne koriste android framework klase tj. da nema android.* imports u okviru klase. “ViewModel” je u direktnoj vezi sa fragmentom/aktivnosti ali neće biti uništen ako je njegov vlasnik uništen (promenom konfiguracije npr. rotacije uredjaja), nova instanca vlasnika će se ponovo povezati na postojeći “ViewModel”. Upravo je to jedna od glavnih namena “ViewModel”-a (nakon što prikupi podatke) da sačuva informacije koje su neophodne za View.

Avoid references to Views in ViewModels.

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).

NAPOMENA:
Iako ViewModel može da preživi promenu konfiguracije (rotacija ekrana…) ipak ne živi beskonačno. “ViewModel” ne može da preživi back dugme ili ubijanje aktivnosti od strane opertativnog sistema ili korisnika. Što vodi do zaključka da ViewModel klasa nije zamena za trajno čuvanje podataka (baza…) ili onSaveInstanceState().
Treba napomenti da je trenutno u alpha verziji SavedStateHandle tzv. “Saved State module for ViewModel” sa kojim možemo da prebacimo kod iz onSaveInstanceStata() metede u ViewModel. Pročitate više o ovome u članku “ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders”.

“Data repository”

Aplikacija može da ima više izvora podataka:

  • Remote: network or the Cloud
  • Local: database or file
  • In-memory cache

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”. Repository nije neka specijalna arhitektonska komponenta već obična klasa koja ima pristup svim izvorima podataka.

Data repository as the single-point entry to your data

Ovaj sloj je vezan za izvore podataka u aplikaciji, i potpuno je nesvestan sloja prezentacije. Repository ima namenu da ViewModel-u uprosti problem pristupa podacima tako što mu obezbedjuje jednostavan API za to.

Repository diagram

Podelite:

Ostavite komentar