Flux架构, 顾名思义表示”流”, 是以数据流为基础.
任何架构最终的目的都是让程序更加有序, 功能便于扩展, Bug容易追踪.
Facebook使用Flux架构来构建客户端Web应用. Flux架构并不是为移动端设计的, 但是我们仍然可以采用这个思想在Android端使用.
基本架构模型如图:
模型主要分为四个模块:
1. View: 视图. 通过调用ActionCreator创建响应用户操作的Action.
2. Action: 事件. View通过ActionCreator发送至Dispatcher, Dispatcher创建Action并分发至EventBus.
3. Dispatcher: 调度器. ActionCreator调用Dispatcher, 传递创建的Action, 使用EventBus, 把Action送到Bus.
4. Store: 状态. 维护一个特定的数据状态, 接收Bus分发Action, 根据Action类型执行不同的业务逻辑. 在完成时, 发出一个ChangeEvent事件. View监听这一事件, 更新显示.
Talk is cheap, show you the code.
非常简单的一个TodoList的App, 数据仅仅只有一个Todo的数组.
项目代码: https://github.com/SpikeKing/MyFluxApp-TodoList
package www.wangchenlong.me.myfluxapp;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import www.wangchenlong.me.myfluxapp.actions.ActionsCreator;
import www.wangchenlong.me.myfluxapp.dispatcher.Dispatcher;
import www.wangchenlong.me.myfluxapp.stores.TodoStore;
/**
* 主UI控件: ToDoList, 使用Flux架构
* Dispatcher调度器, Action事件, Store控制选择
* View调用Action事件, Action发送给Dispatcher进行调度, Dispatcher发送到EventBus
* EventBus进行事件分发, 传递给Store, Store处理完成之后, 发送事件StoreChangeEvent,
* 由EventBus找到主页面View进行更新UI.
*/
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
private static Dispatcher sDispatcher;
private static ActionsCreator sActionsCreator;
private static TodoStore sTodoStore; // 数据存储器, 存储Todo数据的状态
private RecyclerAdapter mListAdapter;
@Bind((R.id.main_layout))
ViewGroup mMainLayout; // 主控件
@Bind(R.id.main_input)
EditText mMainInput; // 编辑控件
@Bind(R.id.main_list)
RecyclerView mMainList; // ListView
@Bind(R.id.main_checkbox)
CheckBox mMainCheck; // 选中按钮
// 添加按钮
@OnClick(R.id.main_add)
void addItem() {
addTodo(); // 添加TodoItem
resetMainInput(); // 重置输入框
}
// 选中按钮
@OnClick(R.id.main_checkbox)
void checkItem() {
checkAll(); // 所有Item项改变选中状态
}
// 清理完成事项
@OnClick(R.id.main_clear_completed)
void clearCompletedItems() {
clearCompleted(); // 清除选中的状态
resetMainCheck();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 设置Layout
ButterKnife.bind(this); // 绑定ButterKnife
initDependencies(); // 创建Flux的核心管理类
// 设置RecyclerView
mMainList.setLayoutManager(new LinearLayoutManager(this));
mListAdapter = new RecyclerAdapter(sActionsCreator);
mMainList.setAdapter(mListAdapter);
}
/**
* Dispatcher调度器, Action事件, Store控制选择
*/
private void initDependencies() {
sDispatcher = Dispatcher.getInstance(new Bus());
sActionsCreator = ActionsCreator.getInstance(sDispatcher);
sTodoStore = TodoStore.getInstance(sDispatcher);
}
@Override
protected void onResume() {
super.onResume();
// 把Subscribe的接口注册到EventBus上面
sDispatcher.register(this);
sDispatcher.register(sTodoStore);
}
@Override
protected void onPause() {
super.onPause();
sDispatcher.unregister(this);
sDispatcher.unregister(sTodoStore);
}
/**
* 添加ToDo项, 向ActionsCreator传递输入文本.
*/
private void addTodo() {
if (validateInput()) {
sActionsCreator.create(getInputText());
}
}
/**
* 重置输入框
*/
private void resetMainInput() {
mMainInput.setText("");
}
/**
* 改变改变所有状态(ActionsCreator)
*/
private void checkAll() {
sActionsCreator.toggleCompleteAll();
}
/**
* 清理选中的项(ActionsCreator)
*/
private void clearCompleted() {
sActionsCreator.destroyCompleted();
}
/**
* 全选中按钮的置换状态
*/
private void resetMainCheck() {
if (mMainCheck.isChecked()) {
mMainCheck.setChecked(false);
}
}
/**
* 更新UI
*/
private void updateUI() {
// 设置适配器数据, 每次更新TodoStore的状态
mListAdapter.setItems(sTodoStore.getTodos());
if (sTodoStore.canUndo()) { // 判断是否可恢复
// 下面的提示条, 恢复删除, 提示信息
Snackbar snackbar = Snackbar.make(mMainLayout, "Element deleted", Snackbar.LENGTH_LONG);
// 恢复按钮
snackbar.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View view) {
sActionsCreator.undoDestroy();
}
});
snackbar.show();
}
}
// 验证输入框是否是空
private boolean validateInput() {
return !TextUtils.isEmpty(getInputText());
}
// 获取输入数据
private String getInputText() {
return mMainInput.getText().toString();
}
@Subscribe
public void onTodoStoreChange(TodoStore.TodoStoreChangeEvent event) {
updateUI();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
View中最核心的部分有两个:
(1) ActionsCreator负责创建Action, 所有方法都被封装在其中, 如ActionsCreator.toggleCompleteAll().
(2) onTodoStoreChange负责接收Store执行过业务逻辑之后的数据, 如TodoStore.getTodos(), 还可以根据状态进行修改, 如TodoStore.canUndo().
package www.wangchenlong.me.myfluxapp.actions;
import www.wangchenlong.me.myfluxapp.dispatcher.Dispatcher;
import www.wangchenlong.me.myfluxapp.model.Todo;
/**
* 事件发生器, 向Dispatcher发送事件类型, 和数据字典.
* <p/>
* Created by wangchenlong on 15/8/17.
*/
public class ActionsCreator {
private static ActionsCreator sInstance;
private final Dispatcher mDispatcher;
private ActionsCreator(Dispatcher dispatcher) {
mDispatcher = dispatcher;
}
public static ActionsCreator getInstance(Dispatcher dispatcher) {
if (sInstance == null) {
sInstance = new ActionsCreator(dispatcher);
}
return sInstance;
}
public void create(String text) {
mDispatcher.dispatch(
TodoActions.TODO_CREATE,
TodoActions.KEY_TEXT, text
);
}
public void destroy(long id) {
mDispatcher.dispatch(
TodoActions.TODO_DESTROY,
TodoActions.KEY_ID, id
);
}
public void undoDestroy() {
mDispatcher.dispatch(
TodoActions.TODO_UNDO_DESTROY
);
}
public void toggleComplete(Todo todo) {
long id = todo.getId();
String actionType = todo.isComplete() ?
TodoActions.TODO_UNDO_COMPLETE : TodoActions.TODO_COMPLETE;
mDispatcher.dispatch(
actionType,
TodoActions.KEY_ID, id
);
}
public void toggleCompleteAll() {
mDispatcher.dispatch(TodoActions.TODO_TOGGLE_COMPLETE_ALL);
}
public void destroyCompleted() {
mDispatcher.dispatch(TodoActions.TODO_DESTROY_COMPLETED);
}
}
ActionsCreator调用Dispatcher的dispatch(), 第一个参数是类型, 其余是数据, 两两组合Key-Value.
package www.wangchenlong.me.myfluxapp.dispatcher;
import com.squareup.otto.Bus;
import www.wangchenlong.me.myfluxapp.actions.Action;
import www.wangchenlong.me.myfluxapp.stores.Store;
/**
* 调度器
* <p/>
* Created by wangchenlong on 15/8/17.
*/
public class Dispatcher {
private final Bus mBus;
private static Dispatcher sInstance;
private Dispatcher(Bus bus) {
mBus = bus;
}
public static Dispatcher getInstance(Bus bus) {
if (sInstance == null) {
sInstance = new Dispatcher(bus);
}
return sInstance;
}
public void register(final Object cls) {
mBus.register(cls);
}
public void unregister(final Object cls) {
mBus.unregister(cls);
}
private void post(final Object event) {
mBus.post(event);
}
// 每个状态改变都需要发送事件, 由View相应, 做出更改
public void emitChange(Store.StoreChangeEvent o) {
post(o);
}
/**
* 调度核心函数
*
* @param type 调度类型
* @param data 数据(Key, Value)
*/
public void dispatch(String type, Object... data) {
if (type == null || type.isEmpty()) { // 数据空
throw new IllegalArgumentException("Type must not be empty");
}
if (data.length % 2 != 0) { // 非Key-Value
throw new IllegalArgumentException("Data must be a valid list of key,value pairs");
}
Action.Builder actionBuilder = Action.type(type);
int i = 0;
while (i < data.length) {
String key = (String) data[i++];
Object value = data[i++];
actionBuilder.bundle(key, value); // 放置键值
}
// 发送到EventBus
post(actionBuilder.build());
}
}
调度器代理了EventBus的功能, 有两个部分:
(1) dispatch()把数据转换成Action, 发送至Bus.
(2) emitChange()把修改后的状态通知发送至Bus, 由View接收负责处理.
package www.wangchenlong.me.myfluxapp.stores;
import com.squareup.otto.Subscribe;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import www.wangchenlong.me.myfluxapp.dispatcher.Dispatcher;
import www.wangchenlong.me.myfluxapp.actions.Action;
import www.wangchenlong.me.myfluxapp.actions.TodoActions;
import www.wangchenlong.me.myfluxapp.model.Todo;
/**
* 状态类, 主要处理所有Todo列表事件的状态
* <p/>
* Created by wangchenlong on 15/8/17.
*/
public class TodoStore extends Store {
private static TodoStore sInstance; // 单例
private final List<Todo> mTodos; // 数据列表
private Todo lastDeleted; // 最近一次删除数据
private TodoStore(Dispatcher dispatcher) {
super(dispatcher);
mTodos = new ArrayList<>();
}
public static TodoStore getInstance(Dispatcher dispatcher) {
if (sInstance == null) {
sInstance = new TodoStore(dispatcher);
}
return sInstance;
}
// 获取数据
public List<Todo> getTodos() {
return mTodos;
}
// 恢复
public boolean canUndo() {
return lastDeleted != null;
}
@Override
@Subscribe
public void onAction(Action action) {
long id;
switch (action.getType()) {
case TodoActions.TODO_CREATE:
String text = ((String) action.getData().get(TodoActions.KEY_TEXT));
create(text);
emitStoreChange(); // 发生改变事件
break;
case TodoActions.TODO_DESTROY:
id = ((long) action.getData().get(TodoActions.KEY_ID));
destroy(id);
emitStoreChange();
break;
case TodoActions.TODO_UNDO_DESTROY:
undoDestroy();
emitStoreChange();
break;
case TodoActions.TODO_COMPLETE:
id = ((long) action.getData().get(TodoActions.KEY_ID));
updateComplete(id, true);
emitStoreChange();
break;
case TodoActions.TODO_UNDO_COMPLETE:
id = ((long) action.getData().get(TodoActions.KEY_ID));
updateComplete(id, false);
emitStoreChange();
break;
case TodoActions.TODO_DESTROY_COMPLETED:
destroyCompleted();
emitStoreChange();
break;
case TodoActions.TODO_TOGGLE_COMPLETE_ALL:
updateCompleteAll();
emitStoreChange();
break;
}
}
private void destroyCompleted() {
Iterator<Todo> iter = mTodos.iterator();
while (iter.hasNext()) {
Todo todo = iter.next();
if (todo.isComplete()) {
iter.remove();
}
}
}
private void updateCompleteAll() {
if (areAllComplete()) {
updateAllComplete(false);
} else {
updateAllComplete(true);
}
}
private boolean areAllComplete() {
for (Todo todo : mTodos) {
if (!todo.isComplete()) {
return false;
}
}
return true;
}
private void updateAllComplete(boolean complete) {
for (Todo todo : mTodos) {
todo.setComplete(complete);
}
}
private void updateComplete(long id, boolean complete) {
Todo todo = getById(id);
if (todo != null) {
todo.setComplete(complete);
}
}
private void undoDestroy() {
if (lastDeleted != null) {
addElement(lastDeleted.clone());
lastDeleted = null;
}
}
private void create(String text) {
long id = System.currentTimeMillis();
Todo todo = new Todo(id, text);
addElement(todo);
Collections.sort(mTodos);
}
private void destroy(long id) {
Iterator<Todo> iter = mTodos.iterator();
while (iter.hasNext()) {
Todo todo = iter.next();
if (todo.getId() == id) {
lastDeleted = todo.clone();
iter.remove();
break;
}
}
}
private Todo getById(long id) {
Iterator<Todo> iter = mTodos.iterator();
while (iter.hasNext()) {
Todo todo = iter.next();
if (todo.getId() == id) {
return todo;
}
}
return null;
}
// 添加数据进入列表
private void addElement(Todo clone) {
mTodos.add(clone);
Collections.sort(mTodos);
}
@Override
public StoreChangeEvent changeEvent() {
return new TodoStoreChangeEvent();
}
public class TodoStoreChangeEvent implements StoreChangeEvent {
}
}
Store最为复杂, onAction()监听Bus所有的Action, 并处理业务逻辑, 最后调用emitStoreChange, 通知页面进行修改.
目前为止, 一整套的循环逻辑都已经完成, 清晰可见, 这就是架构的好处吧.
View -> Action -> Dispatcher -> Store -> View.
想更多的了解Flux架构:
https://facebook.github.io/flux/docs/overview.html
http://lgvalle.xyz/2015/08/04/flux-architecture/
To enjoy it!
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/caroline_wendy/article/details/47783525