Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LiveData<🤖> vs Observable<Rx> vs Channel<Kt> (m...

LiveData<🤖> vs Observable<Rx> vs Channel<Kt> (minimal slides)

All these classes can be used to handle a flow of data in your Android app. This presentation goes through a specific use case, and how these classes differ in their implementations of managing and observing data.

Avatar for Jacques Smuts

Jacques Smuts

September 28, 2019
Tweet

More Decks by Jacques Smuts

Other Decks in Programming

Transcript

  1. Jacques Smuts • ??? • 2014 - Started Coding Android

    • 2018 - Started Coding Kotlin + RxJava • 2019 - Started Using Coroutines + LiveData @JacquesSmuts GDG Cape Town JacquesSmuts.com
  2. Observable< > vs Database Observable< > vs Binding Observable< >

    vs Observable< > vs Flow< > vs LiveData Observable< > vs Observable< > vs
  3. LiveData< > • Built into Android AppCompat Library • Is

    meant to provide up-to-date data to UI • Tied to Activity/Fragment lifecycle to prevent leaks/crashes • Reliable, easy, simple • Good for beginners
  4. LiveData< > • Built into Android AppCompat Library • Is

    meant to provide up-to-date data to UI • Tied to Activity/Fragment lifecycle to prevent leaks/crashes • Reliable, easy, simple • Good for beginners
  5. Slightly Better Activity/ Fragment (onCreate) API Service ViewModel Repository (Database)

    get Email oldEmail@ address.com get Email newEmail@ address.com newEmail@ address.com
  6. LiveData<> Observer class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) {

    userViewModel.liveEmail.observe(this, Observer { email -> updateUi(email) }) } } LifecycleOwner null? val liveEmail: LiveData<String>
  7. LiveData<> Observer class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) {

    userViewModel.liveEmail.observeNonNull(this) { email -> updateUi(email) }) } }
  8. class UserViewModel { val roomDatabaseRepository = RoomDatabaseRepository .getInstance() val liveEmail:

    LiveData<String> get() = roomDatabaseRepository.getLiveEmail() } LiveData<> Observable
  9. class UserViewModel { val _liveEmail = MutableLiveData<String>() fun updateEmail(email: String)

    { _liveEmail.postValue(email) } } Automatically on UI Thread LiveData<> Observable liveEmail.value == [last value]
  10. class UserViewModel { private val _liveEmail = MutableLiveData<String>() val liveEmail:

    LiveData<String> get() = liveEmailMutable fun updateEmail(email: String) { _liveEmail.postValue(email) } } LiveData<> Observable
  11. Observable< > • Difficult to understand at first • Large

    Library • Long History • Powerful and can adapt to any situation
  12. Observable< > • Difficult to understand at first • Large

    Library • Long History • Powerful and can adapt to any situation
  13. RxJava Activity/ Fragment (onStart) API Service Repository (Database) Observable ViewModel

    update Email oldEmail@ address.com State SERVER newEmail@ address.com
  14. class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) { userViewModel.observableState .subscribe

    { state -> updateUi(state) }, { error -> logError(error) } } } Semi-auto error catching RxJava Observer
  15. class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) { userViewModel.observableState .throttleLast(100,

    TimeUnit.Milliseconds) .subscribe { state -> updateUi(state) }, { error -> logError(error) } } } RxJava Observer
  16. class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) { userViewModel.observableState .distinctUntilChanged()

    .subscribe { state -> updateUi(state) }, { error -> logError(error) } } } RxJava Observer
  17. class UserFragment/Activity { val disposables = CompositeDisposable() override fun onCreate(savedInstanceState:

    Bundle?) { disposables.add(userViewModel.observableState .subscribe { state -> updateUi(state.email) }, { error -> logError(error) }) } } disposables.clear() RxJava Observer
  18. class UserViewModel { val roomDatabaseRepository = RoomDatabaseRepository .getInstance() val observableEmail:

    Flowable<String> get() = roomDatabaseRepository.getEmail() } Observable<String> RxJava Observable
  19. class UserViewModel { private val _stateObservable = PublishSubject<State>() val stateObservable

    get()= _stateObservable.hide() fun updateState(email: String) { _stateObservable.onNext(State(email)) } } RxJava Observable
  20. RxJava Activity/ Fragment (onStart) API Service Repository (Database) Observable ViewModel

    update Email oldEmail@ address.com State SERVER newEmail@ address.com
  21. Channel< >/Flow • Experimental • Amazing community • Readable Code

    • One small part of Coroutines Library • Is The Future
  22. Channel< >/Flow • Experimental • Amazing community • Readable Code

    • One small part of Coroutines Library • Is The Future
  23. Channel/Flow Activity/ Fragment (onCreate) API Service Repository (Database) Flow ViewModel

    update Email oldEmail@ address.com State SERVER newEmail@ address.com
  24. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .collect { state -> updateUi(state) } } } } All coroutines in scope cleaned up by 
 coroutineContext.cancelChildren()
  25. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .throttleLast(100, TimeUnit.Millisecond) .collect { state -> updateUi(state) } } } }
  26. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .debounce() .collect { state -> updateUi(state) } } } }
  27. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .distinctUntilChanged() .collect { state -> updateUi(state) } } } }
  28. class UserViewModel { val roomDatabaseRepository = RoomDatabaseRepository .getInstance() val emailFlow:

    Flow<String> get() = roomDatabaseRepository.getEmail() } Coroutines Observable
  29. class UserViewModel { val stateObservable = BroadcastChannel<State>(1) fun updateState(email: String)

    { stateObservable.offer(State(email)) } } Coroutines Observable Returns true/false, yeets exception
  30. class UserViewModel, CoroutineScope by DefaultScope() { val stateObservable = BroadcastChannel<State>(1)

    fun updateState(email: String) { stateObservable.offer(State(email)) } } Coroutines Observable
  31. class UserViewModel, CoroutineScope by DefaultScope() { val stateObservable = BroadcastChannel<State>(1)

    fun updateState(email: String) { launch { stateObservable.send(State(email)) } } } Coroutines Observable
  32. class UserViewModel, CoroutineScope by DefaultScope() { val stateObservable = BroadcastChannel<State>(1)

    fun updateState(email: String) { stateObservable.offer(State(email)) } } Coroutines Observable
  33. class UserViewModel, CoroutineScope by DefaultScope() { private val _stateObservable =

    BroadcastChannel<State>(1) val stateObservable get()= _stateObservable.asFlow() fun updateState(email: String) { _stateObservable.offer(State(email)) } } Coroutines Observable Channels inside, Flows outside
  34. Channel/Flow Activity/ Fragment (onCreate) API Service Repository (Database) Flowable ViewModel

    update Email oldEmail@ address.com State SERVER newEmail@ address.com
  35. Comparisons LiveData Easy to Read Yes Lifecycle/Cleanup Fully Auto Nullable

    Always Scope UI Only Saves Last Value Yes Threading UI Only Stable / Reliable Yes Guides / Docs Good Multiplatform No
  36. Comparisons LiveData RxJava Easy to Read Yes No Lifecycle/Cleanup Fully

    Auto Fully Manual Nullable Always Configurable Scope UI Only Anywhere Saves Last Value Yes Configurable Threading UI Only Mostly Easy Stable / Reliable Yes Yes Guides / Docs Good Meh Multiplatform No Sorta
  37. Comparisons LiveData RxJava Coroutines Easy to Read Yes No Mostly

    Lifecycle/Cleanup Fully Auto Fully Manual Semi-Auto Nullable Always Configurable IDE-enforced Scope UI Only Anywhere Anywhere Saves Last Value Yes Configurable Configurable Threading UI Only Mostly Easy Easy Stable / Reliable Yes Yes Sorta Guides / Docs Good Meh Quality, but few Multiplatform No Sorta Mostly
  38. Comparisons LiveData RxJava Coroutines Easy to Read Lifecycle/Cleanup Nullable Scope

    Saves Last Value Threading Stable / Reliable Guides / Docs Multiplatform ⏳ ⏳
  39. Comparisons LiveData RxJava Coroutines Who should use it? Beginners++ Intermediate++

    Everyone* Final Rating Team Setup? Small / New Large Production App Supportive team Any setup* All Future Kotlin Apps ⏳ *Willing to accept risk of cutting edge
  40. Testing LiveData RxJava Coroutines Ease of basic testing Easy Almost

    easy Easy Ease of complex testing ? Doable Doable Readability of Tests Easy Almost Easy Easy