$30 off During Our Annual Pro Sale. View Details »

Architecture Components Distilled

Architecture Components Distilled

Presentation about Android Architecture Components, offering internal and practical considerations for Lifecycles, ViewModels and LiveDatas.

Presentation given at following events

- GDGSP Android Meetup #53
- GDG Devfest Paraná 2017

Ubiratan Soares
PRO

November 09, 2017
Tweet

More Decks by Ubiratan Soares

Other Decks in Programming

Transcript

  1. ARCHITECTURE
    COMPONENTS
    DISTILLED
    Ubiratan Soares
    November / 2017

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. SEVERAL YEARS
    LATER …

    View Slide

  9. View Slide

  10. View Slide

  11. AFTER SOME
    USAGE ...

    View Slide

  12. View Slide

  13. LIFECYCLE
    ("the good")

    View Slide

  14. 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"

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  19. VIEWMODEL
    ("the ugly")

    View Slide

  20. 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"

    View Slide

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

    View Slide

  22. // 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)
    }

    View Slide

  23. // 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)
    }

    View Slide

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

    View Slide

  25. 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
    }
    }

    View Slide

  26. 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
    }
    }

    View Slide

  27. 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
    }
    }

    View Slide

  28. / 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)
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. // 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
    }
    }
    }
    }

    View Slide

  38. // 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
    }
    }
    }
    }

    View Slide

  39. // 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
    }
    }
    }
    }

    View Slide

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

    View Slide

  41. LIVEDATA
    ("the bad")

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  56. HOW ABOUT
    STATE SAVING?

    View Slide

  57. WHEN AN ANDROID
    ACTIVITY DIES ???

    View Slide

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

    View Slide

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

    View Slide

  60. MAYBE ALL
    OF THEM !!!

    View Slide

  61. "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"

    View Slide

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

    View Slide

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

    View Slide

  64. CONCLUSIONS

    View Slide

  65. 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!

    View Slide

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

    View Slide

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

    View Slide

  68. https://speakerdeck.com/ubiratansoares

    View Slide

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

    View Slide

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

    View Slide