1 of 68

Tehnici avansate pentru

dezvoltarea aplicațiilor mobile

7 - Compose - Arhitectura aplicațiilor Android

2 of 68

Principii arhitecturale

2

3 of 68

Principii arhitecturale

  • Separarea responsabilităților

  • Generarea UI-ului de la un model

  • Sursă unică de adevăr

  • Flux de date unidirecțional

7 - Compose - Arhitectura aplicațiilor Android

3

4 of 68

Separarea responsabilităților

  • Principiul separării responsabilităților

  • Aplicația este împărțită în clase de funcționalități
    • fiecare cu responsabilități separate

  • Greșeală: tot codul într-o Activitate

7 - Compose - Arhitectura aplicațiilor Android

4

5 of 68

Separarea responsabilităților

  • Clasele UI trebuie să conțină cât mai puțin cod
    • doar interacțiunea cu UI și cu SO

  • Activitățile
    • pot fi distruse oricând de SO
    • trebuie să minimizăm dependența de activități

7 - Compose - Arhitectura aplicațiilor Android

5

6 of 68

Generarea UI-ului de la un model de date

  • Principiu: UI-ul este generat pornind de la modelul de date

  • Modelele de date gestionează datele unei aplicații

7 - Compose - Arhitectura aplicațiilor Android

6

7 of 68

Generarea UI-ului de la un model de date

  • Independente de elementele de UI și de alte componente
    • Nu sunt afectate de ciclul de viață al UI-ului
      • Activitate omorâtă => nu afectează modelul de date

  • Sunt distruse dacă SO omoară aplicația

7 - Compose - Arhitectura aplicațiilor Android

7

8 of 68

Generarea UI-ului de la un model de date

  • Modele persistente sunt ideale
    • Datele nu se pierd cand SO omoară aplicația
    • Aplicația funcționează când conexiunea la rețea este slabă

  • Aplicație mai robustă, mai ușor de testat

7 - Compose - Arhitectura aplicațiilor Android

8

9 of 68

Sursă unică de adevăr (SSOT)

  • Sursă unică de adevăr pentru un tip de date
    • Deține datele
    • Singura care poate modifica datele

  • Expune datele printr-un tip immutable

  • Expune funcții pentru modificarea datelor
    • sau primește evenimente

7 - Compose - Arhitectura aplicațiilor Android

9

10 of 68

Sursă unică de adevăr (SSOT)

  • Avantaje:
    • Centralizează modificarea unor anumite date
    • Protejează datele pentru a nu fi modificate din exterior
    • Modificarea datelor poate fi urmărită mai ușor
      • identificat bug-uri

7 - Compose - Arhitectura aplicațiilor Android

10

11 of 68

Flux de date unidirecțional (UDF)

  • Starea se transmite într-o singură direcție

  • Evenimentele care modifică starea se transmit în direcția opusă

7 - Compose - Arhitectura aplicațiilor Android

11

12 of 68

Flux de date unidirecțional (UDF)

  • Datele se transmit:
    • de la SSOT către UI

  • Evenimentele se transmit:
    • de la UI către SSOT
    • la SSOT datele sunt modificate și expuse ca read-only

  • Consistența datelor, prevenirea erorilor, debugging

7 - Compose - Arhitectura aplicațiilor Android

12

13 of 68

Arhitectura recomandată

13

14 of 68

Arhitectura recomandată

7 - Compose - Arhitectura aplicațiilor Android

14

15 of 68

Arhitectura recomandată

  • Fiecare aplicație ar trebui să aibă cel puțin 2 layere:
    • Layer UI
      • afișează datele pe ecran
      • independent de date
    • Layer de date
      • stochează, preia și expune datele

7 - Compose - Arhitectura aplicațiilor Android

15

16 of 68

Arhitectura recomandată

  • Layer-ul UI depinde de layer-ul de date

  • Layer-ul domeniu (opțional)
    • Pentru a simplifica și reutiliza interacțiunile dintre UI și date

7 - Compose - Arhitectura aplicațiilor Android

16

17 of 68

UI Layer

  • Rol de a afișa datele aplicației pe ecran
  • Când datele se schimbă => actualizare UI

  • Datele se schimbă când:
    • Utilizatorul interacționează cu aplicația
      • Exemplu: apasă un buton
    • Se primește input extern
      • Exemplu: un răspuns primit prin rețea

7 - Compose - Arhitectura aplicațiilor Android

17

18 of 68

UI Layer

18

19 of 68

UI Layer

  • Layer-ul UI include:
    • Elemente de UI
      • Compose (recomandat)
      • Views
    • State holders
      • De exemplu: ViewModel
      • mențin starea, o expun către UI
      • gestionează logica aplicației

7 - Compose - Arhitectura aplicațiilor Android

19

20 of 68

Data Layer

20

21 of 68

Data Layer

  • Repositories & surse de date

  • Conține logica de business
    • Modul cum aplicația creează, stochează și schimbă datele

  • Include clase repository
    • Pot conține 0 sau mai multe surse de date

7 - Compose - Arhitectura aplicațiilor Android

21

22 of 68

Data Layer

  • Fiecare tip de date => o clasă repository
    • Exemplu: Clasa MoviesRepository pentru filme

  • Mai multe surse de date pentru același tip de date

  • Fiecare clasă de sursă de date ar trebui să lucreze cu o singură sursă de date
    • Exemplu: fișier, rețea, bază de date locală

7 - Compose - Arhitectura aplicațiilor Android

22

23 of 68

Data Layer

  • Clasele repository
    • Expun date către restul aplicației
    • Centralizează modificarea datelor
    • Rezolvă conflictele între mai multe surse de date
    • Abstractizează sursele de date pentru restul aplicației
    • Includ logica de business

7 - Compose - Arhitectura aplicațiilor Android

23

24 of 68

ViewModel

24

25 of 68

ViewModel

25

26 of 68

ViewModel

  • Transformă datele aplicației în starea UI-ului

  • Stochează și expune starea către UI
    • starea UI-ului

  • Principiu arhitectural : generarea UI-ului de la un model

7 - Compose - Arhitectura aplicațiilor Android

26

27 of 68

ViewModel

  • Când activitatea este distrusă și recreată de SO
    • Obiectele ViewModel nu sunt distruse
      • Ex: în timpul schimbărilor de configurare
    • datele stocate de ViewModel sunt reținute în aplicație

  • Extindem clasa ViewModel
    • stocăm datele în ea

7 - Compose - Arhitectura aplicațiilor Android

27

28 of 68

Starea UI-ului

  • UI-ul este reprezentarea vizuală a stării UI-ului
  • Orice modificare a stării => modificarea UI-ului
  • UI = combinarea între elementele UI și starea UI

7 - Compose - Arhitectura aplicațiilor Android

28

29 of 68

Imutabilitatea stării UI-ului

  • Starea este immutable
    • mai multe surse nu pot modifica starea în același timp
  • UI-ul are un singur rol:
    • să citească starea și să actualizeze elementele de UI

7 - Compose - Arhitectura aplicațiilor Android

29

30 of 68

Imutabilitatea stării UI-ului

  • Regulă: nu trebuie modificată starea direct din cadrul UI-ului
    • Doar dacă UI-ul este singura sursă a datelor

  • Încălcarea regulii
    • => mai multe surse de adevăr pentru aceleași date
      • => inconsistența datelor, bug-uri subtile

7 - Compose - Arhitectura aplicațiilor Android

30

31 of 68

ViewModel - exemplu

  • În fișierul build.gradle.kts (Module :app)

7 - Compose - Arhitectura aplicațiilor Android

31

32 of 68

ViewModel - exemplu

  • În pachetul ui - extindem clasa ViewModel

  • În pachetul ui → o clasă de tip data → starea UI

7 - Compose - Arhitectura aplicațiilor Android

32

33 of 68

StateFlow

33

34 of 68

StateFlow

  • Flux observabil de date

  • Conține starea curentă și actualizările de stare
    • proprietatea value = starea curentă

  • Pentru a scrie/actualiza starea:
    • modificăm proprietatea value a clasei MutableStateFlow

7 - Compose - Arhitectura aplicațiilor Android

34

35 of 68

StateFlow

  • Folosit pentru a gestiona starea UI

  • Model producător - consumator
    • ViewModel scrie starea UI
    • Composables citesc starea UI

35

36 of 68

StateFlow - exemplu

  • În ViewModel salvăm starea UI
    • MutableStateFlow
  • => Composables pot asculta schimbările de stare UI
  • Starea va supraviețui schimbării de configurație

7 - Compose - Arhitectura aplicațiilor Android

36

37 of 68

Backing Property

37

38 of 68

Backing property

  • Proprietate privată mutable
  • Proprietate publică immutable
  • Getter-ul returnează valoarea proprietății private

7 - Compose - Arhitectura aplicațiilor Android

38

39 of 68

Folosirea backing property în ViewModel

  • În clasa ViewModel
    • Folosim backing property
    • Expunem starea UI în mod immutable

  • O proprietate privată mutable
    • accesibilă și editabilă doar din interiorul clasei

7 - Compose - Arhitectura aplicațiilor Android

39

40 of 68

Folosirea backing property în ViewModel

  • O proprietate publică immutable
    • poate avea doar getter, NU setter
    • returnează valoarea proprietății private
    • => permite doar citirea stării UI din afara ViewModel
    • protejează starea UI din cadrul ViewModel

7 - Compose - Arhitectura aplicațiilor Android

40

41 of 68

Backing property - exemplu

  • _uiState este starea UI-ului (privată)
    • accesibilă și editabilă doar din cadrul clasei
  • uiState (publică)
    • pentru accesarea în mod read-only a stării de către UI
  • _uiState.asStateFlow()
    • transformă fluxul de stare mutable într-unul immutable

7 - Compose - Arhitectura aplicațiilor Android

41

42 of 68

Fluxul de date unidirecțional

42

43 of 68

Fluxul de date unidirecțional (UDF)

  • Se schimbă starea
    • => se recompun părțile din arborele UI care folosesc starea

  • Composables - acceptă starea și expun evenimente

  • Modelul fluxului de date unidirecțional se potrivește bine cu Compose

7 - Compose - Arhitectura aplicațiilor Android

43

44 of 68

Fluxul de date unidirecțional (UDF)

  • Model de design

  • Starea se transmite în jos
  • Evenimentele se transmit în sus

  • Decuplare între
    • Composables care afișează starea (UI)
    • părțile aplicației care stochează și modifică starea (VM)

7 - Compose - Arhitectura aplicațiilor Android

44

45 of 68

Bucla de actualizare a UI-ului

7 - Compose - Arhitectura aplicațiilor Android

45

46 of 68

Bucla de actualizare a UI-ului

1. Generarea evenimentelor

    • UI-ul generează un eveniment

și îl transmite în sus către ViewModel

      • De exemplu: o apăsare de buton

    • Un eveniment generat de alte layere ale aplicației
      • De exemplu: sesiunea utilizatorului a expirat

7 - Compose - Arhitectura aplicațiilor Android

46

47 of 68

Bucla de actualizare a UI-ului

2. Actualizarea stării:

    • Handler-ul de eveniment poate schimba

starea

3. Afișarea stării

    • Componenta care deține starea o transmite în jos și UI-ul o afișează

7 - Compose - Arhitectura aplicațiilor Android

47

48 of 68

Fluxul de date unidirecțional (UDF)

  • ViewModel transformă datele aplicației în stare UI

  • ViewModel deține starea și o expune către UI

  • UI-ul transmite evenimentele către ViewModel

7 - Compose - Arhitectura aplicațiilor Android

48

49 of 68

Fluxul de date unidirecțional (UDF)

  • ViewModel procesează evenimentele și actualizează starea

  • Starea este trimisă înapoi la UI pentru a fi afișată

  • Procesul se repetă pentru orice eveniment care modifică starea

7 - Compose - Arhitectura aplicațiilor Android

49

50 of 68

Exemplu de UDF

50

51 of 68

Stare UI

7 - Compose - Arhitectura aplicațiilor Android

51

52 of 68

ModelView

7 - Compose - Arhitectura aplicațiilor Android

52

53 of 68

Expunerea stării către UI

53

54 of 68

Expunerea stării către UI

  • Composable are ca argument o instanță a ViewModel
  • Accesăm starea UI folosind collectAsState()

7 - Compose - Arhitectura aplicațiilor Android

54

55 of 68

Expunerea stării către UI

  • Accesăm starea UI folosind collectAsState()
    • Colectează ultima valoare din StateFlow
    • Obiect State
    • StateFlow.value este folosită ca valoare inițială

  • Când este postată o nouă stare în StateFlow
    • valoarea returnată (State) se modifică
    • => o recompoziție

7 - Compose - Arhitectura aplicațiilor Android

55

56 of 68

Folosirea unei valori din stare

  • Starea este folosită pentru a actualiza composables (UI-ul)

7 - Compose - Arhitectura aplicațiilor Android

56

57 of 68

Pasarea evenimentului către ViewModel

  • Pasăm evenimentul de la UI către ViewModel
  • Pasăm starea de la ViewModel către UI

57

58 of 68

Pasarea evenimentului către ViewModel

  • În UI, la apăsarea butonului se apelează funcția checkUserGuess() din ModelView

7 - Compose - Arhitectura aplicațiilor Android

58

59 of 68

Actualizarea stării

  • În ViewModel funcția checkUserGuess()
    • verifică dacă cuvântul e corect
    • modifică proprietatea isGuessedWordWrong din starea UI-ului

7 - Compose - Arhitectura aplicațiilor Android

59

60 of 68

Actualizarea stării

  • În funcția checkUserGuess()
    • MutableStateFlow<T>.update() actualizează MutableStateFlow.value
    • copy() face o copie a stării cu un câmp modificat

7 - Compose - Arhitectura aplicațiilor Android

60

61 of 68

Pasarea stării către UI

  • În composable folosim proprietatea isGuessedWordWrong din starea UI-ului

7 - Compose - Arhitectura aplicațiilor Android

61

62 of 68

UDF

62

63 of 68

Bibliografie

7 - Compose - Arhitectura aplicațiilor Android

63

64 of 68

Cuvinte cheie

  • Principii arhitecturale
  • UI Layer
  • Data Layer
  • ViewModel
  • Starea UI
  • StateFlow
  • Backing Property
  • Flux de date unidirecțional
  • Evenimente

7 - Compose - Arhitectura aplicațiilor Android

64

64

65 of 68

UDF - exemplu

  • onUserGuessChanged - callback de eveniment care se transmite de la UI la ViewModel
    • callback-ul actualizează starea (cuvântul ghicit)
    • apoi starea este citită și afișată de UI

65

66 of 68

UDF - exemplu

  • userGuess - cuvântul ghicit
  • updateUserGuess - funcția care actualizeză cuvântul ghicit

66

67 of 68

UDF - exemplu

  • Composable - argumente userGuess și callback

  • Apelare: dăm valoarea proprietății și funcția din ViewModel

67

68 of 68

UDF - exemplu

În Composable:

  • Valoarea câmpului este valoarea proprietății din ViewModel
  • Callback-ul onValueChange este funcția din ViewModel

68