ViewModel

ViewModel klasa je klasa u Androidu koja se koristi za upravljanje korisničkim interfejsom. Ideja je da se ono što je vezano za prikazivanje sadržaja korisničkog interfejsa odvoji u posebnu klasu. Takođe, ViewModel je zadužen da čuva osetljive informacije od promena kroz koje aplikacija da prođe tokom izvršavanja. Recimo, u ranijem pristupu ako okrenemo telefon, aktivnosti će da se unište i pokrenu ispočetka, što znači da će neke informacije potencijalno biti izgubljene. Na primer, ako imamo štopericu i tokom rada štoperice okrenemo ekran, onda će štoperica krenuti da meri vreme ispočetka. Ovakvo ponašanje nije poželjno.
Kao i za neke druge biblioteke, mora se ViewModel ugraditi u projekat preko Gradle fajla. Potrebne informacije se mogu naći u zvaničnoj dokumentaciji na sledećem linku.
Postoji nekoliko načina da se doda ViewModel. Jedan način podrazumeva standardni pristup - napravimo potklasu klase ViewModel i onda instanciramo njene objekte. Međutim, ovaj pristup nije dobar iz prostog razloga što ne obezbeđuje čuvanje informacija kada dođe do promene (npr. rotacije ekrana). U drugom načinu, pozivamo metod klase ViewModelProvider koji će napraviti ViewModel (ako on već ne postoji) i onda proslediti njegovu referencu. Taj ViewModel se vezuje za neku aktivnost ili fragment i postoji dokle god on postoji ili dok se ne odvoji od aktivnosti, kao u slučaju fragmenta.

Važno! U ViewModel nikako ne držati reference na fragmente i aktivnosti, a ni na binding objekte, jer ti podaci ne preživljavaju promene u konfiguraciji.

Osnovna struktura ViewModel klase je ovakva:

class AViewModel : ViewModel() {
    val osetljivPodatak_1: tip_1
    val osetljivPodatak_2: tip_2
    ....

    init {
        ...
    }

    fun metodNadOsetljivimPodacima(argumenti...) {
        ...
    }

}

Kao i sa binding elementima, i ViewModel instanciramo kao lateinit iznad svih metoda aktivnosti ili fragmenta, a potom ga u metodu onCreate instanciramo na ovaj način:

viewModel = ViewModelProvider(this).get(AViewModel::class.java)

Sada kada pozivamo metode koje rade nad osetljivim podacima, koristimo naš ViewModel.

LiveData

LiveData je tip podataka koji čuva informacije i koji je "svestan" životnih ciklusa aktivnosti i fragmenata. LiveData se povezuje sa odgovarajućim posmatračkim objektima koji prate promene koje se dešavaju u podacima. Svesnost životnih ciklusa znači da će LiveData da obaveštava posmatrače samo onda kada su njihovi fragmenti ili aktivnosti u aktivnim stanjima (Started i Resumed).
LiveData objekti su nemutabilni, što znači da prilikom promene mora da se generiše novi objekat. Postoji i MutableLiveData koji su mutabilni. Obe klase su generičke i zahtevaju navođenje tipa objekta koji se čuva.
Promenljive koje bismo čuvali u prethodnim primerima ćemo sada čuvati kao LiveData ili MutableLiveData. Recimo, kod bi mogao da izgleda ovako:

val nekiTekst = MutableLiveData<String>()
Podatak koji se čuva je u atributu value. Recimo, ako želimo da upišemo tekst:
val nekiTekst = MutableLiveData<String>()
nekiTekst.value = "Upisali smo tekst"

Sada ćemo videti kako se posmatrački objekti (Observer) vezuju za LiveData. Posmatrački objekat je objekat koji prati promene LiveData promenljivih i reaguje u skladu sa tim promenama. Na sledećem primeru se postavlja posmatrački objekat koji ažurira vrednost nekog tekstualnog polja kad god nastupi promena nekog broja koji smo sačuvali u LiveData. Kod posmatračkih objekata se mora biti oprezan zbog konfiguracionih promena. Ako korisnik napusti aplikaciju, ali je ne zatvori, tada proces koji crta trenutni fragment na ekranu se uništava, iako sam fragment ostaje da "živi". Iz tog razloga, kada imamo posmatrački objekat koji menja izgled korisničkog interfejsa, potrebno je proslediti mu vlasnika životnog ciklusa (viewLifeCycleOwner) i držati posmatračke objekte u metodu onCreateView metoda.

viewModel.broj.observe(viewLifeCycleOwner, Observer<Int> {
    novaVrednost -> binding.brojText.text = novaVrednost.toString()
})

Metod observe prati promene vrednosti promenljive broj. Ona prima vlasnika životnog ciklusa i posmatrački objekat koji je zadužen da obradi promenu. Unutar posmatračkog objekta smo definisali lambda funkciju koja kao argument prima novu vrednost broja i postavlja određeno tekstualno polje na tu vrednost.

Vezivanje ViewModel

Android Studio nudi mogućnost korisnicima da vežu ViewModel i njegove promenljive na isti način na koji smo vezali promenljive preko binding objekata. Da bi se to uradilo, potrebno je otići u XML fajl odgovarajućeg fragmenta. Tada unutar taga layout, a iznad ostalih tagova, potrebno je definisati naš ViewModel. Osnovna struktura je ovakva:

<layout ...>

   <data>

       <variable
           name="viewModel"
           type=".paket1.paket2...AViewModel" />
   </data>
...

Sada je potrebno, kao i ostale objekte koje smo vezali, ViewModel objekat koji smo instancirali vezati na sledeći način:

binding.viewModel = viewModel

Takođe, možemo i pozive metoda setOnClickListener da smenimo pozivom metoda direktno u XML fajlu iz ViewModel. Tada je potrebno atribut android:onClick podesiti na sledeći način:

android:onClick="@{() -> viewModel.onButton1Click()}"

Osim vezivanja ViewModel, moguće je vezivati i LiveData. Sve što treba je na odgovarajući atribut (najčešće android:text) dodati nešto nalik ovome:

android:text="@{viewModel.atribut}"

Ovo je jako korisno, jer čini kod preglednijim. Tada nije potrebno pisati posmatrače kada je potrebno ažurirati promenu određene promenljive, već će to kompajler za nas da uradi. Da bi ovo proradilo, potrebno je samo još definisati atribut lifecycleOwner od binding, kao u sledećem primeru.

binding.viewModel = ...
binding.lifecycleOwner = viewLifecycleOwner