Flux for Android / Droidcon Boston 2018

Flux for Android / Droidcon Boston 2018

This is the slide for Droidcon Boston session "Flux: Utilize unidirectional data flow to think less and scale faster".

By utilizing a unidirectional data flow, we aim to think less and scale faster. Flux is all about having unidirectional data flow throughout the application using Flux components such as Action, Dispatcher, Store, and more.

While there are a lot of more benefits as well, declaring those components feature based rather than screen or view based, you may be able to make use of already-declared components and the logics, which helps you to make your app DRY, write less code than you otherwise have to write, and modify only once place when there needs a change in your logic or specification.

Below is a list of official Flux documentation links:

* facebook/flux: Application Architecture for Building User Interfaces
https://github.com/facebook/flux

* flux/examples/flux-concepts at master · facebook/flux
https://github.com/facebook/flux/tree/master/examples/flux-concepts#flux-parts

---

And below are the example implementations for Flux architecture and some slides:

* satorufujiwara/architecture-patterns-samples
https://github.com/satorufujiwara/architecture-patterns-samples

* ogaclejapan/FluxArchitectureSample: Sample app for Flux Architecture
https://github.com/ogaclejapan/FluxArchitectureSample

* Flux de Relax :) // Speaker Deck
https://speakerdeck.com/ogaclejapan/flux-de-relax

F9856cc7a15ed2cb9e6ebfab41fdf1cf?s=128

Shohei Kawano

March 27, 2018
Tweet

Transcript

  1. Flux for Android @shaunkawano

  2. "

  3. Flux for Web Android ? "

  4. Flux for Android ? "

  5. What is Flux?

  6. •Introduced by Facebook(@F8 in 2014) •Originally for building client-side web

    applications •Data flows in one direction Flux
  7. Unidirectional Data Flow https://github.com/facebook/flux/tree/master/examples/flux-concepts#overview “The most important concept is that

    data flows in one direction.”
  8. Flow of Data Action View Dispatcher Store http://facebook.github.io/flux/docs/in-depth-overview.html#structure-and-data-flow

  9. Flow of Data Action View Dispatcher Store Action Creator

  10. Components

  11. Action View Dispatcher Store Action Creator

  12. Action View Dispatcher Store Action Creator

  13. Action View Dispatcher Store Action Creator • Render UI based

    on Store states/values • Store subscriber • Has no states
  14. Action View Dispatcher Store Action Creator • Helper • Creates

    Actions • Passes Actions to Dispatcher • Has no states
  15. Action View Dispatcher Store Action Creator • Container • Created

    by ActionCreator • Dispatched by Dispatcher • Delivered to Store
  16. Action View Dispatcher Store Action Creator • Central hub for

    all of the data flows • Dispatches Actions to Stores • Has no states
  17. Action View Dispatcher Store Action Creator • State manager •

    Changes its states and data only by receiving Actions • Has states / internal logic
  18. Store The ONLY state manager

  19. Action View Dispatcher Store Action Creator • State manager •

    Changes its states and data only by receiving Actions • Has states / internal logic
  20. Action View Dispatcher Store Action Creator

  21. Why? Action View Dispatcher Store Action Creator

  22. •Not complicated diagram or boilerplates •You know where to look

    •Code consistency •Easy to test •Easy to debug •…
  23. DRY

  24. Construct Flux Dataflow Action View Dispatcher Store Action Creator

  25. image: https://material.io/guidelines/components/lists.html#lists-specs

  26. Libraries in this example •RxJava •Dagger •Data Binding •greenrobot/EventBus •uber/AutoDispose

    •trello/navi
  27. None
  28. View View

  29. Activity Fragment Views(e.g. RecyclerView) Custom View View

  30. onCreate(savedInstanceState: Bundle?) View

  31. class UserListActivity : BaseActivity() { private val binding by lazy

    { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) }
  32. class UserListActivity : BaseActivity() { private val binding by lazy

    { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) }
  33. <!—- activity_user_list.xml —-> <?xml version="1.0" encoding="utf-8"?> <layout … > <data>

    <import type="android.view.View"/> <variable name="isLoading" type="boolean" /> </data> … <android.support.v7.widget.RecyclerView android:id=“@+id/user_list" … /> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{isLoading ? View.VISIBLE : View.GONE}" … /> …
  34. class UserListActivity : BaseActivity() { private val binding by lazy

    { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) }
  35. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) }
  36. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var userListStore: UserListStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } 1. Initialize View 2. Subscribe State 3. Call ActionCreator’s functions View 3 Steps in Activity / Fragment
  37. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var userListStore: UserListStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } 1. Initialize View View
  38. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) }
  39. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) }
  40. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) } View 2. Subscribe State
  41. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) } View 2. Subscribe “Loading” State
  42. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // Subscribe State loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } }
  43. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // Subscribe State loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } }
  44. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize View binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // Subscribe State loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } }
  45. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // initView() binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // subscribeState() loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } }
  46. class UserListActivity : Activity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() } …
  47. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() } … View 3. Call ActionCreators’ functions
  48. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } …
  49. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } … View
  50. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } … Action View Dispatcher Store Action Creator
  51. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } … Action View Dispatcher Store Action Creator
  52. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } … Action Creator
  53. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() // Create & Dispatch Action actionCreator.showUserList() } … Action View Dispatcher Store Action Creator
  54. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator Action View Dispatcher Store Action Creator
  55. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator
  56. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator
  57. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator
  58. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator
  59. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } LoadingAction Action
  60. Action @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private

    val api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } }
  61. Action sealed class LoadingAction(activity: Activity) { val activityKey: String =

    activity.componentName.className class Idle(activity: Activity) : LoadingAction(activity) class Loading(activity: Activity) : LoadingAction(activity) }
  62. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } UserListAction Action
  63. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action
  64. Action sealed class UserListAction { class ShowUserList(val userList: UserList) :

    UserListAction() class Error(val error: Throwable) : UserListAction() }
  65. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator Action View Dispatcher Store Action Creator
  66. Action sealed class UserListAction { class ShowUserList(val userList: UserList) :

    UserListAction() class Error(val error: Throwable) : UserListAction() } Action View Dispatcher Store Action Creator
  67. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Action Creator
  68. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  69. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  70. Dispatcher class Dispatcher { private val eventBus = EventBus.builder() .addIndex(DispatcherIndex())

    .throwSubscriberException(BuildConfig.DEBUG) .build() fun dispatch(event: Any) { eventBus.post(event) } fun register(observer: Any) { eventBus.register(observer) } fun unregister(observer: Any) { eventBus.unregister(observer) }
  71. class Dispatcher { private val eventBus = EventBus.builder() .addIndex(DispatcherIndex()) .throwSubscriberException(BuildConfig.DEBUG)

    .build() fun dispatch(event: Any) { eventBus.post(event) } fun register(observer: Any) { eventBus.register(observer) } fun unregister(observer: Any) { eventBus.unregister(observer) } Dispatcher For Broadcasting Actions: • RxJava • EventBus … and more?
  72. Action sealed class UserListAction { class ShowUserList(val userList: UserList) :

    UserListAction() class Error(val error: Throwable) : UserListAction() } Action View Dispatcher Store Action Creator
  73. Dispatcher class Dispatcher { private val eventBus = EventBus.builder() .addIndex(DispatcherIndex())

    .throwSubscriberException(BuildConfig.DEBUG) .build() fun dispatch(event: Any) { eventBus.post(event) } fun register(observer: Any) { eventBus.register(observer) } fun unregister(observer: Any) { eventBus.unregister(observer) } Action View Dispatcher Store Action Creator
  74. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  75. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatch LoadingAction Dispatcher
  76. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  77. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  78. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  79. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  80. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store For Observing Lifecycle Events: • AAC: Lifecycle • RxLifecycle: Navi … and more? Store
  81. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  82. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  83. @PerActivity class LoadingStore @Inject constructor(private val activityKey: String, dispatcher: Dispatcher,

    navi: NaviComponent) { private val loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { … } Store
  84. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  85. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  86. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  87. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  88. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatch UserListAction Dispatcher
  89. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  90. @PerActivity class UserListActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi, private val activity: Activity) { fun showUserList() { dispatcher.dispatch(LoadingAction.Loading(activity)) api.fetchUserList().subscribeBy( onSuccess= { userList -> dispatcher.dispatch(UserListAction.ShowUserList(userList)) dispatcher.dispatch(LoadingAction.Idle(activity)) }, onError = { e -> dispatcher.dispatch(UserListAction.Error(e)) dispatcher.dispatch(LoadingAction.Idle(activity)) }) } } Dispatcher
  91. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  92. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  93. @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) { private

    val userList = ObservableArrayList<User>() … fun addOnListChangedCallback(callback: ObservableList.OnListChangedCallback<ObservableList<User>>) { userList.addOnListChangedCallback(callback) } fun removeOnListChangedCallback(callback: ObservableList.OnListChangedCallback<ObservableList<User>>) { userList.removeOnListChangedCallback(callback) } Store
  94. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  95. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  96. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  97. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val listSubject = PublishSubject.create<List<User>>() val listState: Observable<List<User>> = listSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { listSubject.onNext(action.userList.data) } } } } Action View Dispatcher Store Action Creator
  98. @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) { private

    val listSubject = PublishSubject.create<List<User>>() val listState: Observable<List<User>> = listSubject.hide() init { Streaming Loading State Change Using RxJava Store
  99. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  100. @PerActivity class LoadingStore @Inject constructor( … ) { private val

    loadingSubject = PublishSubject.create<Boolean>() val loadingState: Observable<Boolean> = loadingSubject.hide() init { … } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey == activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store
  101. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // initView() binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // subscribeState() loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } } View
  102. class UserListActivity : BaseActivity() { @Inject lateinit var actionCreator: UserListActionCreator

    @Inject lateinit var loadingStore: LoadingStore @Inject lateinit var userListAdapter: UserListAdapter private val binding by lazy { DataBindingUtil.setContentView<ActivityUserListBinding>( this, R.layout.activity_user_list) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // initView() binding.fab.setOnClickListener { doSomething() } binding.userList.adapter = userListAdapter binding.userList.layoutManager = LinearLayoutManager(this) // subscribeState() loadingStore.loadingState.autoDisposable(scope) .subscribe { binding.isLoading = it } } View
  103. <!—- activity_user_list.xml —-> <?xml version="1.0" encoding="utf-8"?> <layout … > <data>

    <import type="android.view.View"/> <variable name="isLoading" type="boolean" /> </data> … <android.support.v7.widget.RecyclerView android:id=“@+id/user_list" … /> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{isLoading ? View.VISIBLE : View.GONE}" … /> …
  104. <!—- activity_user_list.xml —-> <?xml version="1.0" encoding="utf-8"?> <layout … > <data>

    <import type="android.view.View"/> <variable name="isLoading" type="boolean" /> </data> … <android.support.v7.widget.RecyclerView android:id=“@+id/user_list" … /> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{isLoading ? View.VISIBLE : View.GONE}" … /> …
  105. @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) { private

    val listSubject = PublishSubject.create<List<User>>() val listState: Observable<List<User>> = listSubject.hide() init { Streaming UserList Change Using Data Binding Store
  106. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { userList.addAll(action.userList.data) } is UserListAction.Error -> { … } } …
  107. @PerActivity class UserListAdapter @Inject constructor( private val store: UserListStore, navi:

    NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val listChangeCallback = … init { navi.addListener(Event.CREATE, { store.addOnListChangedCallback(listChangeCallback) }) navi.addListener(Event.DESTROY, { store.removeOnListChangedCallback(listChangeCallback) }) } … View
  108. @PerActivity class UserListAdapter @Inject constructor( private val store: UserListStore, navi:

    NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val listChangeCallback = … init { navi.addListener(Event.CREATE, { store.addOnListChangedCallback(listChangeCallback) }) navi.addListener(Event.DESTROY, { store.removeOnListChangedCallback(listChangeCallback) }) } … View
  109. @PerActivity class UserListAdapter @Inject constructor( private val store: UserListStore, navi:

    NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val listChangeCallback = … init { navi.addListener(Event.CREATE, { store.addOnListChangedCallback(listChangeCallback) }) navi.addListener(Event.DESTROY, { store.removeOnListChangedCallback(listChangeCallback) }) } … View
  110. @PerActivity class UserListAdapter @Inject constructor( private val store: UserListStore navi:

    NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val listChangeCallback = … init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.binding.executePendingBindings() } override fun getItemCount() = store.userListSize … View
  111. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val listSubject = PublishSubject.create<List<User>>() val listState: Observable<List<User>> = listSubject.hide() init { navi.addListener(Event.CREATE, { dispatcher.register(this)}) navi.addListener(Event.DESTROY, { dispatcher.unregister(this)}) } @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserListAction) { when (action) { is UserListAction.ShowUserList -> { listSubject.onNext(action.userList.data) } } } } Action View Dispatcher Store Action Creator
  112. Constructed Flux Dataflow Action View Dispatcher Store Action Creator

  113. None
  114. ‘Favorite’? User Detail?

  115. None
  116. None
  117. None
  118. None
  119. None
  120. None
  121. None
  122. ‘Favorite’? User Detail?

  123. @PerActivity class UserListAdapter @Inject constructor( private val store: UserListStore, navi:

    NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.binding.executePendingBindings() } … View
  124. @PerActivity class UserListAdapter @Inject constructor( private val userActionCreator: UserActionCreator, private

    val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.executePendingBindings() } … View
  125. @PerActivity class UserListAdapter @Inject constructor( private val userActionCreator: UserActionCreator, private

    val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.executePendingBindings() } … View
  126. @PerActivity class UserListAdapter @Inject constructor( private val userActionCreator: UserActionCreator, private

    val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.executePendingBindings() } … View
  127. @PerActivity class UserListAdapter @Inject constructor( private val userActionCreator: UserActionCreator, private

    val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.executePendingBindings() } … View
  128. @PerActivity class UserActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi) { fun favorite(user: User) { api.favorite(user.id).subscribeBy( onComplete = { dispatcher.dispatch(UserAction.Favorite(user.id)) }, … } } Action Creator
  129. @PerActivity class UserActionCreator @Inject constructor(private val dispatcher: Dispatcher, private val

    api: SomeApi) { fun favorite(user: User) { api.favorite(user.id).subscribeBy( onComplete = { dispatcher.dispatch(UserAction.Favorite(user.id)) }, … } } Action Creator
  130. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { … } … @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserAction) { when (action) { is UserAction.Favorite -> { userList.find { it.id == action.targetUserId }?.let { // Update or replace with new user } } } …
  131. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { … } … @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserAction) { when (action) { is UserAction.Favorite -> { userList.find { it.id == action.targetUserId }?.let { // Update or replace with new user } } } …
  132. None
  133. None
  134. ‘Favorite’? User Detail?

  135. @PerActivity class UserListAdapter @Inject constructor( private val userActionCreator: UserActionCreator, private

    val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { … } override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.executePendingBindings() } … View
  136. @PerActivity class UserListAdapter @Inject constructor( private val activityActionCreator: ActivityActionCreator, private

    val userActionCreator: UserActionCreator, private val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { … override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.root.setOnClickListener { activityActionCreator.startUserDetailActivity(user) } holder.binding.executePendingBindings() } … View
  137. @PerActivity class UserListAdapter @Inject constructor( private val activityActionCreator: ActivityActionCreator, private

    val userActionCreator: UserActionCreator, private val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { … override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.root.setOnClickListener { activityActionCreator.startUserDetailActivity(user) } holder.binding.executePendingBindings() } … View
  138. @PerActivity class UserListAdapter @Inject constructor( private val activityActionCreator: ActivityActionCreator, private

    val userActionCreator: UserActionCreator, private val store: UserListStore, navi: NaviComponent): RecyclerView.Adapter<RecyclerView.ViewHolder>() { … override fun onBindViewHolder( … ) { … holder.binding.user = store.getUserAt(position) holder.favIcon.setOnClickListener { userActionCreator.favorite(user) } holder.binding.root.setOnClickListener { activityActionCreator.startUserDetailActivity(user) } holder.binding.executePendingBindings() } … View
  139. @PerActivity class ActivityActionCreator @Inject constructor(private val activity: Activity) { fun

    startUserDetailActivity(user: User) { val intent = UserDetailActivity.createIntent(activity, user) activity.startActivity(intent) } fun start…Activity ( … ) { … } fun start…Activity ( … ) { … } } Action Creator
  140. None
  141. None
  142. class UserDetailActivity : BaseActivity() { @Inject lateinit var actionCreator: UserActionCreator

    @Inject lateinit var loadingStore: LoadingStore private val binding by lazy { DataBindingUtil.setContentView<ActivityUserDetailBinding>( this, R.layout.activity_user_detail) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() } … View
  143. class UserDetailActivity : BaseActivity() { @Inject lateinit var actionCreator: UserActionCreator

    @Inject lateinit var loadingStore: LoadingStore private val binding by lazy { DataBindingUtil.setContentView<ActivityUserDetailBinding>( this, R.layout.activity_user_detail) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() subscribeState() } … View
  144. class UserDetailActivity : BaseActivity() { @Inject lateinit var actionCreator: UserActionCreator

    @Inject lateinit var loadingStore: LoadingStore private val binding by lazy { DataBindingUtil.setContentView<ActivityUserDetailBinding>( this, R.layout.activity_user_detail) } override fun onCreate(savedInstanceState: Bundle?) { initView() … } private fun initView() { binding.fab.setOnClickListener { userActionCreator.favorite(user) } } … View
  145. class UserDetailActivity : BaseActivity() { @Inject lateinit var actionCreator: UserActionCreator

    @Inject lateinit var loadingStore: LoadingStore private val binding by lazy { DataBindingUtil.setContentView<ActivityUserDetailBinding>( this, R.layout.activity_user_detail) } override fun onCreate(savedInstanceState: Bundle?) { initView() … } private fun initView() { binding.fab.setOnClickListener { userActionCreator.favorite(user) } } … View
  146. None
  147. None
  148. None
  149. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { … } … @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserAction) { when (action) { is UserAction.Favorite -> { userList.find { it.id == action.targetUserId }?.let { // Update or replace with new user } } } …
  150. Store @PerActivity class UserListStore @Inject constructor(dispatcher: Dispatcher, navi: NaviComponent) {

    private val userList = ObservableArrayList<User>() init { … } … @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserAction) { when (action) { is UserAction.Favorite -> { userList.find { it.id == action.targetUserId }?.let { // Update or replace with new user } } } …
  151. DRY

  152. None
  153. Code Consistency

  154. 1. Initialize View 2. Subscribe State 3. Call ActionCreators’ functions

    Steps in Activity / Fragment View
  155. 1. initView() 2. subscibeState() 3. actionCreator.doSomething() Steps in Activity /

    Fragment View
  156. 1. (Create & Dispatch LoadingAction) 2. Create & Dispatch Success

    / Error Action 3. (Create & Dispatch Idle Action) Steps in ActionCreator Action Creator
  157. The logic inside Store becomes the intuitive specification

  158. • Kotlin sealed classes & “when” keyword • Actions with

    descriptive names
  159. @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: LoadingAction) { if (action.activityKey ==

    activityKey) { when (action) { is LoadingAction.Loading -> { loadingSubject.onNext(true) } is LoadingAction.Idle -> { loadingSubject.onNext(false) } … Store When given action is Loading, then the next loading state is “true”, if the action is Idle, then “false”.
  160. Store @Subscribe(threadMode = ThreadMode.MAIN) fun on(action: UserAction) { when (action)

    { is UserAction.Favorite -> { userList.find { it.id == action.targetUserId }?.let { // Update or replace with new user } } } … When given action is User’s Favorite Action, then find target user from user list and update as favorited (if there is).
  161. Dispatcher

  162. Dispatcher class Dispatcher { private val eventBus = EventBus.builder() .addIndex(DispatcherIndex())

    .throwSubscriberException(BuildConfig.DEBUG) .build() fun dispatch(event: Any) { eventBus.post(event) } fun register(observer: Any) { eventBus.register(observer) } fun unregister(observer: Any) { eventBus.unregister(observer) } Action View Dispatcher Store Action Creator
  163. Dispatcher class Dispatcher { private val eventBus = EventBus.builder() .addIndex(DispatcherIndex())

    .throwSubscriberException(BuildConfig.DEBUG) .build() fun dispatch(event: Any) { eventBus.post(event) } fun register(observer: Any) { eventBus.register(observer) } fun unregister(observer: Any) { eventBus.unregister(observer) } Action View Dispatcher Store Action Creator
  164. Dispatcher Store Action Action

  165. Store Action Creator

  166. Store The ONLY state manager

  167. Summary

  168. •Flux is for utilizing unidirectional data flow by declaring the

    components •Store is the only state manager •Makes your code DRY and consistent with minimal boilerplates Flux Summary
  169. Flux for Android @shaunkawano Thank you!