Flux Demo 源码解析

Flux on Android 的提出者 lgvalle 很早就在 github 上开源了 Flux Demo:

https://github.com/lgvalle/android-flux-todo-app

这篇博客将对该项目做一次源码解析。

我的另一篇博客:

Flux 架构简析

包结构

Flux Package Architecture

Flux Demo 的包结构相当简洁,文件也不多,除了 View 层的 TodoActivity 与 TodoRecyclerAdapter,model 包下的 Todo,其余 3 个 package 分别对应着 Flux 架构的三个模块 Action,Dispatcher,Store,具体分析如下:

  • actions : Action 模块
    • Action : Action 实体,由 Action 的唯一标识 type 与所含数据 data 组成
    • ActionsCreator : Action 生成器,提供业务逻辑调用,发送 Action
    • TodoActions : Todo 业务相关 Action 数据
  • dispatcher : Dispatcher 模块
    • Dispatcher : Action 分发类,内部含有一个 bus 用于 Action 的分发
  • model
    • Todo
  • stores : Store 模块
    • Store : Store 抽象基类,需要子类复写其抽象方法 onAction() 接收 Dispatcher 分发的 Action 事件,同时持有 Dispatcher,用于分发 storeChangeEvent
    • TodoStore : Todo 业务 Store 实现类,持有业务逻辑数据 todos,并通过 onAction(Action action) 接收 Dispatcher 分发的 Action,同时暴漏 View 所需要的数据接口 getTodos(),canUndo()
  • TodoActivity
  • TodoRecyclerAdapter

UML 类图

类图能很清晰的反映出各个类/模块之间的关系(plantuml 生成的类图一眼看去确实比较乱,建议大家放大查看),与上一篇博客所说的相吻合,类图中可以很清晰的看到 View → ActionsCreator → Action → Dispatcher → Store → storeChangeEvent → View 所形成的闭环。

Flux UML Class

一次 Flux 闭环

最后我们以 创建一个 Todo 事项 举例来说明 Flux 架构的工作流程,并分析具体的代码实现。

  1. TodoActivity.java → 用户输入
  2. TodoActivity.java → 用户点击创建按钮
  3. TodoActivity.java → 调用方法 addTodo()
  4. TodoActivity.java → 调用 ActionsCreator 的方法 create(String text),传入用户输入内容
  5. ActionsCreator.java → 调用 Dispatcher 的方法 dispatch(String type, Object... data)
  • 传入 Action 的 key(type),标识这个 Action 是创建操作的 TodoActions.TODO_CREATE,以及 Action 的 data,即 TodoActions.KEY_TEXT 与 text 这对 key-value
  • 在 dispatch 方法中会将传入的 Action 相关数据组装成一个 Action 对象
  1. ActionsCreator.java → 调用 Dispatcher 的方法 post(final Object event),传入上一步组装好的 Action 对象
  2. Dispatcher.java → 调用 Bus 的方法 post(Object event),传入 Action 对象,这里是通过 otto 库发送了 Action
  3. TodoStore.java → onAction(Action action) 接收到 Bus 发送的 Action 对象,根据 action.getType() 判断要做的操作为 TodoActions.TODO_CREATE
  4. TodoStore.java → 执行 create(String text) 方法创建一个 Todo 对象,添加到 todos 数据集中,方法接收的 text 参数是通过 Action 对象得到的,记得我们在第 5 步时将 text 组装到了 Action 对象中
  5. Store.java → 执行 emitStoreChange()
  6. Store.java → 调用 Dispatcher 的方法 emitChange(Store.StoreChangeEvent o),传入通过 changeEvent() 方法生成的 StoreChangeEvent 对象,在这个例子中 TodoStore 类重写了 changeEvent() 方法,返回 TodoStoreChangeEvent 对象
  7. Dispatcher.java → 调用 Bus 的方法 post(Object event),传入第 11 步传入的 StoreChangeEvent 对象,这里是通过 otto 库发送了 StoreChangeEvent
  8. TodoActivity.java → onTodoStoreChange(TodoStore.TodoStoreChangeEvent event) 方法接收到 Bus 发送的 TodoStoreChangeEvent 对象
  9. TodoActivity.java → 执行 updateUI() 从 TodoStore 获取数据(第 9 步时更新了 TodoStore 中的数据)更新界面

小结

一次简单的创建 Todo 的过程被我们拆解成出 14 个步骤,看起来很麻烦,但不难从中发现 Flux 架构极的单向数据流传输与可定制性。我们在 ActionsCreator 中对处理各种各样的业务逻辑,根据结果分发不同的 Action,每一个 Action 对应的都是一条干净,清晰,完整的数据流传递。我们在 Store 中针对 Action 响应数据更新,最后再通知 View 更新,这个过程中也存在强大的可定制性,比如在上面的例子中,我们创建完成后提交的 StoreChangeEvent 不再是通用的 TodoStoreChangeEvent,而是针对 TODO_CREATE Action 发送特定的 TodoCreateStoreChangeEvent,View 层响应 TodoCreateStoreChangeEvent 执行的操作则是从 TodoStore 中取出最新的一条数据,调用 notifyItemInserted 动态的添加到 View 界面,这样用户体验无疑会好很多。

Flux 架构的特点与优势在于 闭环 与 单向数据流,在 Flux 架构上写业务是一个通畅的过程,但 Flux 的定位还是与 MVP 类似,只是业务逻辑层的架构,如果放在当前大热的 CleanArchitecture 中,仅相当于 presentation 这一层,domain 层与 data 层则不是 Flux 关心的事情,或许后面会写一篇关于 CleanArchitecture 的简介。