Slide 1

Slide 1 text

ARCHITECTURE COMPONENTS DISTILLED Ubiratan Soares November / 2017

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

SEVERAL YEARS LATER …

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

AFTER SOME USAGE ...

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

LIFECYCLE ("the good")

Slide 14

Slide 14 text

LIFECYCLE ROLE "A single abstraction on top of an Android component lifecycle (Activity, Fragment) that unifies both states and events transitions representations in a component-independent fashion"

Slide 15

Slide 15 text

INITIALIZED CREATED STARTED RESUMED DESTROYED ON_CREATE ON_START ON_RESUME ON_DESTROY ON_PAUSE ON_STOP ENUMS

Slide 16

Slide 16 text

LIFECYCLE OWNER FRAGMENT + getLifecycle( ) FRAGMENT ACTIVITY PROCESS LIFECYCLE + getCurrentState( ) + removeObserver( ) + addObserver( ) LIFECYCLE REGISTRY Dependency ๏ Colaborator Return Type ๏ Actual State ๏ Observers ๏ Parent States

Slide 17

Slide 17 text

Favors composition over inheritance class YourLifecycleObserver : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { // Some action at start } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onStop() { // Some action at stop } } // At your Activity / Fragment lifecycle.addObserver(YourLifecycleObserver())

Slide 18

Slide 18 text

TYPICAL USAGES FOR OBSERVERS Auto-release for RxJava2 Disposables Auto bind/unbind presenters into/from their passive Views Auto register/unregister into/from some external framework, outside Activity/Fragment code Auto track some screen view at every screen resume ETC

Slide 19

Slide 19 text

VIEWMODEL ("the ugly")

Slide 20

Slide 20 text

VIEWMODEL ROLE "An abstraction that provides a brigde between an Android component (Activity, Fragment) and the "business logic" of the application, with a proper lifecycle scoped according the related component itself"

Slide 21

Slide 21 text

class YourViewModel(val repository: SomeRepository) : ViewModel() { val items by lazy { repository.retrieveFromDatabase() } fun fetchData(): List { return items } }

Slide 22

Slide 22 text

// Your Activity lateinit var viewModel : YourViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) viewModel = ViewModelProviders.of(this) .get(YourViewModel::class.java) }

Slide 23

Slide 23 text

// Your Activity lateinit var viewModel : YourViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) viewModel = ViewModelProviders.of(this) .get(YourViewModel::class.java) }

Slide 24

Slide 24 text

A CONSTRAINT If you want your ViewModel to have a non-default constructor, you must use a Factory to create it

Slide 25

Slide 25 text

class YourViewModel(val repository: SomeRepository) : ViewModel() { fun fetchData(): List { return repository.retrieveFromNetwork() } } object YourViewModelFactory : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here return YourViewModel(repository) as T } }

Slide 26

Slide 26 text

class YourViewModel(val repository: SomeRepository) : ViewModel() { fun fetchData(): List { return repository.retrieveFromNetwork() } } object YourViewModelFactory : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here return YourViewModel(repository) as T } }

Slide 27

Slide 27 text

class YourViewModel(val repository: SomeRepository) : ViewModel() { fun fetchData(): List { return repository.retrieveFromNetwork() } } object YourViewModelFactory : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here return YourViewModel(repository) as T } }

Slide 28

Slide 28 text

/ Your Activity lateinit var viewModel : YourViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) viewModel = ViewModelProviders.of(this, YourViewModelFactory) .get(YourViewModel::class.java) }

Slide 29

Slide 29 text

finish( ) ViewModelProviders .of(this) .get(VMClass) onCleared( ) ✓ ON_CREATE ✓ ON_START ✓ ON_RESUME ✓ ON_PAUSE ✓ ON_STOP ✓ ON_DESTROY ✓ ON_CREATE ✓ ON_START ✓ ON_RESUME ✓ ON_PAUSE ✓ ON_STOP ✓ ON_DESTROY ACTIVITY INSTANCE ACTIVITY INSTANCE INSTANCE LIFECYCLE Device Rotation ViewModelProviders .of(this) .get(VMClass) VIEWMODEL INSTANCE

Slide 30

Slide 30 text

VIEW MODEL PROVIDERS ★ of(Activity) ★ of(Fragment) ★ of(Activity, Factory) ★ of(Fragment, Factory) VIEWMODEL PROVIDER + get(Class) VIEWMODEL STORE ๏ Map VIEW MODEL STORES ★ holderFragmentFor(Activity) ★ holderFragmentFor(Fragment) ★ Static Factory Method + Public Instance Method ๏ Colaborator HOLDER FRAGMENT ๏ ViewModelStore + getViewModeStore( ) Return Type ๏ ViewModelProvider.Factory ๏ ViewModelStore

Slide 31

Slide 31 text

VIEW MODEL PROVIDERS ★ of(Activity) ★ of(Fragment) ★ of(Activity, Factory) ★ of(Fragment, Factory) VIEWMODEL PROVIDER + get(Class) VIEWMODEL STORE ๏ Map VIEW MODEL STORES ★ holderFragmentFor(Activity) ★ holderFragmentFor(Fragment) ★ Static Factory Method + Public Instance Method ๏ Colaborator HOLDER FRAGMENT ๏ ViewModelStore + getViewModeStore( ) Return Type ๏ ViewModelProvider.Factory ๏ ViewModelStore

Slide 32

Slide 32 text

VIEWMODEL TL;DR A ViewModel has a dedicated lifecycle, which is provided according the scope of the related component A ViewModel scope will stand longer than the related Android component itself (if needed) : a ViewModel survives configuration changes, backed by a retained Fragment

Slide 33

Slide 33 text

VIEWMODEL IN PRACTICE Should not store any reference for a passive view Should store and provide data related to user interface state Plays well with a single source of truth for data retrieving / processing (eg Repository Pattern) onCleared( ) can be used to release colaborators Should not have any Android framework related code

Slide 34

Slide 34 text

// ViewModels.kt inline fun FragmentActivity.withFactory( crossinline factory: () -> ViewModelProvider.Factory) { ViewModelProviders.of(this, factory()).get(VM::class.java) }

Slide 35

Slide 35 text

// ViewModels.kt inline fun FragmentActivity.withFactory( crossinline factory: () -> ViewModelProvider.Factory) { ViewModelProviders.of(this, factory()).get(VM::class.java) }

Slide 36

Slide 36 text

// ViewModels.kt inline fun FragmentActivity.withFactory( crossinline factory: () -> ViewModelProvider.Factory) { ViewModelProviders.of(this, factory()).get(VM::class.java) }

Slide 37

Slide 37 text

// SomeFactory.kt object SomeFactory { operator fun invoke(): ViewModelProvider.Factory { return object : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here! return YourViewModel(repository) as T } } } }

Slide 38

Slide 38 text

// SomeFactory.kt object SomeFactory { operator fun invoke(): ViewModelProvider.Factory { return object : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here! return YourViewModel(repository) as T } } } }

Slide 39

Slide 39 text

// SomeFactory.kt object SomeFactory { operator fun invoke(): ViewModelProvider.Factory { return object : ViewModelProvider.Factory { override fun create(modelClass: Class?): T { val repository = // Retrieve your repository here! return YourViewModel(repository) as T } } } }

Slide 40

Slide 40 text

KOTLIN AWESOMENESS // At your Activity val viewModel by withFactory { SomeFactory() }

Slide 41

Slide 41 text

LIVEDATA ("the bad")

Slide 42

Slide 42 text

LIVEDATA ROLE "A container that aims to provide the Observer Pattern for data, letting some observer be notified of data changes at proper lifecycle state (Started or Resumed)"

Slide 43

Slide 43 text

interface SomeRepository { // Note : Room can return data as a LiveData !!! fun retrieveFromDatabase(): LiveData> }

Slide 44

Slide 44 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items : LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return items } }

Slide 45

Slide 45 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items : LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return items } }

Slide 46

Slide 46 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items : LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return items } }

Slide 47

Slide 47 text

// At your Activity / Fragment val viewModel by withFactory { SomeFactory() } viewModel .fetchData() .observe(this, Observer { // Update your UI here } )

Slide 48

Slide 48 text

LIVEDATA TL;DR Publishes data updates to observers only at STARTED or RESUMED lifecycle states Ensures that a destroyed observer will not get any updates It not holds any data history, only the actual data state We can perform a few functional operations over LiveData via the Transformations API

Slide 49

Slide 49 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items: LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return Transformations.map(items) { items -> toRowModels(items) } } } fun toRowModels(items: List): List { return items.map { it -> convertToRow(it) } }

Slide 50

Slide 50 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items: LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return Transformations.map(items) { items -> toRowModels(items) } } } fun toRowModels(items: List): List { return items.map { it -> convertToRow(it) } }

Slide 51

Slide 51 text

class YourViewModel( private val repository: SomeRepository) : ViewModel() { private val items: LiveData> by lazy { repository.retrieveFromDatabase() } fun fetchData(): LiveData> { return Transformations.map(items) { items -> toRowModels(items) } } } fun toRowModels(items: List): List { return items.map { it -> convertToRow(it) } }

Slide 52

Slide 52 text

EXTENDING LIVEDATA "You should derive from LiveData only when you need to adapt some external, probably async data source at your repository implementation level"

Slide 53

Slide 53 text

MutableLiveData LiveData MediatorLiveData "What is the difference all ??" vs vs

Slide 54

Slide 54 text

IMPLEMENTATION INTENT LiveData Stores some data in an immutable way MutableLiveData Allows some client object to modify the contents of data - setValue( ) to update from the Android main thread - postValue( ) to update from another thread MediatorLiveData An abstraction to combine two or more LiveDatas in a desired way

Slide 55

Slide 55 text

LIVEDATA IN PRACTICE A RxJava for dummies the masses (???) Only publishes results on Android MainThread Offers a "bridge" to RxJava2 reactive types, but it should be avoided (personal opinion) Confusing roles for MutableLiveData and LiveData (personal opinion)

Slide 56

Slide 56 text

HOW ABOUT STATE SAVING?

Slide 57

Slide 57 text

WHEN AN ANDROID ACTIVITY DIES ???

Slide 58

Slide 58 text

ACTIVITY DEATH REASON TRIGGER USER EXPECTATION Navigation, back or up USER App on a new state Swipe-off from recents USER App on a new state Explicit finish( ) USER (call to action) App on a new state Configuration changes (rotation, locale, etc) DEVICE / USER App on the same state as before Resources reclaim, application in background ANDROID SYSTEM App on the same state as before Process death in extreme conditions, app in background ANDROID SYSTEM App on the same state as before

Slide 59

Slide 59 text

ViewModel Bundle onSaveInstanceState( ) Storage (DB, Files, Prefs) "Which approach should I use for effective screen state saving after all ???"

Slide 60

Slide 60 text

MAYBE ALL OF THEM !!!

Slide 61

Slide 61 text

"Bundle, ViewModel or Local storage may serve as holder for the actual screen state, but each one of them has pros and cons to be dealt with. A comprehensive strategy may use all three ways together : the answer will be given by the application context"

Slide 62

Slide 62 text

STATE SAVING STRATEGY MAY HANDLE RESOURCES RECLAIMS ? HANDLE CONFIG CHANGES ? IS DATA READY FOR THE SCREEN ? AMOUNT OF DATA AVAILABLE RESISTS SYSTEM CRASHING / REBOOT DEMANDS IO ACCESS? Bundle YES YES PROBABLY YES SMALL (order of KB) NO NO ViewModel NO (Process Death) YES PROBABLY YES LARGE (order of MB) NO NO Local Storage YES YES PROBABLY NO LARGER (order of GB) YES YES

Slide 63

Slide 63 text

SAVING STATE MECHANISM COMPREHENSIVE USAGE Bundle Store only a small information footprint that you need to rebuild the entire screen state from the other available means, usually information generated by user interactions (inputed texts, actual page from a large list, actual list position, etc) ViewModel Store related-and-processed data for screen, acessed previously from a single source of truth (Networking, Database, etc) behind a repository Local Storage Store data as soon you get it. Data format may not be UI-related at all, and can be adapted for the UI at ViewModel or Repository level

Slide 64

Slide 64 text

CONCLUSIONS

Slide 65

Slide 65 text

FINAL REMARKS Choose your components wisely! Do not misuse / overabuse ViewModels Use an Observer Pattern implementation to communicate between ViewModels and Activity / Fragments ( ) ViewModels should be a very tiny layer : leverage some presentation strategy to avoid fat classes!

Slide 66

Slide 66 text

Architecture Components Docs https://developer.android.com/topic/libraries/architecture/ index.html Exploring the Architecure Components API https://goo.gl/WKE1sC ViewModels : Persistence, onSaveInstanceState( ), Restoring UI State and Loaders https://goo.gl/8iqdci

Slide 67

Slide 67 text

ViewModels and LiveData - Patterns and Anti-Patterns https://goo.gl/nkVRYU Architecture Components Pitfalls https://goo.gl/GkBcbz Android LiveData QuickLook https://goo.gl/MqviKK Codelabs https://goo.gl/hbMZjy

Slide 68

Slide 68 text

https://speakerdeck.com/ubiratansoares

Slide 69

Slide 69 text

UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy Google Developer Expert for Android Teacher, speaker, etc, etc

Slide 70

Slide 70 text

THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares