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

LiveData<🤖> vs Observable<Rx> vs Channel<Kt>

LiveData<🤖> vs Observable<Rx> vs Channel<Kt>

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
  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
  5. 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
  6. 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
  7. 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
  8. Slightly Better Activity/ Fragment (onCreate) API Service ViewModel Repository (Database)

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

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

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

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

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

    userViewModel.liveEmail.observeNonNull(this) { email -> updateUi(email) }) } }
  14. LiveData<> Observer class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) {

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

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

    { _liveEmail.postValue(email) } } Automatically on UI Thread LiveData<> Observable
  17. class UserViewModel { val _liveEmail = MutableLiveData<String>() fun updateEmail(email: String)

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

    LiveData<String> get() = liveEmailMutable fun updateEmail(email: String) { _liveEmail.postValue(email) } } LiveData<> Observable
  19. class UserViewModel { private val _liveEmail = MutableLiveData<String>() val liveEmail:

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

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

    Library • Long History • Powerful and can adapt to any situation
  22. class UserFragment/Activity { override fun onCreate(savedInstanceState: Bundle?) { userViewModel.observableState .subscribe

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

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

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

    Bundle?) { disposables.add(userViewModel.observableState .subscribe { state -> updateUi(state.email) }, { error -> logError(error) }) } } RxJava Observer
  26. 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
  27. class UserFragment/Activity { val disposables = CompositeDisposable() override fun onCreate(savedInstanceState:

    Bundle?) { disposables.add(userViewModel.observableState .subscribe { state -> updateUi(state.email) }, { error -> logError(error) }) } } RxJava Observer
  28. class UserFragment/Activity { val disposables = CompositeDisposable() override fun onCreate(savedInstanceState:

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

    Flowable<String> get() = roomDatabaseRepository.getEmail() } RxJava Observable
  30. class UserViewModel { val roomDatabaseRepository = RoomDatabaseRepository .getInstance() val observableEmail:

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

    get()= _stateObservable.hide() fun updateState(email: String) { _stateObservable.onNext(State(email)) } } RxJava Observable
  32. class UserViewModel { private val _stateObservable = PublishSubject<State>() val stateObservable

    get()= _stateObservable.hide() fun updateState(email: String) { _stateObservable.onNext(State(email)) } } RxJava Observable
  33. Channel< >/Flow • Experimental • Amazing community • Readable Code

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

    • One small part of Coroutines Library • Is The Future
  35. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .collect { state -> updateUi(state) } } } }
  36. 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()
  37. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

    onCreate(savedInstanceState: Bundle?) { launch { userViewModel.stateFlow .collect { state -> updateUi(state) } } } }
  38. 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) } } } }
  39. Channel/Flow Observer class UserFragment/Activity, CoroutineScope by MainScope() { override fun

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

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

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

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

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

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

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

    fun updateState(email: String) { stateObservable.offer(State(email)) } } Coroutines Observable
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. Comparisons LiveData RxJava Coroutines Easy to Read Lifecycle/Cleanup Nullable Scope

    Saves Last Value Threading Stable / Reliable Guides / Docs Multiplatform ⏳ ⏳
  55. 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
  56. 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