继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

实践移动端的 Flux 架构

饮歌长啸
关注TA
已关注
手记 188
粉丝 46
获赞 152

任何架构最终目的都是让程序更加有序, 功能便于扩展, Bug容易追踪.

Facebook使用Flux架构来构建客户端的Web应用. Flux架构并不是为移动端设计的, 但是我们仍然可以采用这个思想在Android端使用. Flux是数据驱动型架构, 在以数据为核心的场景中使用非常合适, 不过Facebook好像把Flux架构应用于所有产品, 无论是前端还是移动端. 最新Facebook开发的ReactNative中, 就是使用Flux架构为核心, 也是开源的, 可以阅读RN的代码了解所有内容.

Flux架构, 顾名思义表示流, 是以数据流为基础.


本文源码的Github下载地址

模型主要分为四个模块:

  1. View: 视图, 根据用户交互(Use Interaction)的内容创建响应事件, 调用活动创建器(ActionCreator)的方法, 发送响应用户操作的事件.

  2. ActionCreator: 活动创建器, 把活动的类型和数据发送给调度器(Dispatcher), 根据类型和数据创建活动(Action). 也可以进行网络操作(WebApi), 完成后, 再发送数据.

  3. Dispatcher: 调度器, 把接收到的活动, 通过总线(EventBus), 把活动发送出去, 由存储器(Store)根据活动的类型和数据, 修改数据模型.

  4. Store: 存储器, 维护特定的数据状态, 接收总线分发的活动, 根据活动类型执行不同的业务逻辑. 在完成时, 发出数据修改(ChangeEvent)的事件, 视图监听这一事件, 更新显示内容.

Talk is cheap, show you the code.
只有通过代码才能真正的了解架构的意义, 我写了一个基于Flux架构的ToDoList.
页面发送各种增删改查的事件, 通过Flux模型, 修改本地存储的数据, 再反馈回页面.

视图

视图(View): 视图是一个Activity, 负责响应用户交互(Use Interaction)的事件, 发送到给活动创建器(ActionCreator); 同时接收修改数据(ChangeEvent)事件, 更新页面.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class MainActivity extends AppCompatActivity {

    // ...

    // 添加数据
    @OnClick(R.id.main_add) void addItem() {
        addTodo(); // 添加TodoItem
        resetMainInput(); // 重置输入框
    }

    // 选中数据
    @OnClick(R.id.main_checkbox) void checkItem() {
        checkAll(); // 所有Item项改变选中状态
    }

    @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();

        // 把订阅接口注册到EventBus
        sDispatcher.register(this);
        sDispatcher.register(sTodoStore);
    }

    @Override
    protected void onPause() {
        super.onPause();

        // 解除订阅接口
        sDispatcher.unregister(this);
        sDispatcher.unregister(sTodoStore);
    }

    // 改变改变所有状态(ActionsCreator)
    private void checkAll() {
        sActionsCreator.toggleCompleteAll();
    }

    // 清理选中的项(ActionsCreator)
    private void clearCompleted() {
        sActionsCreator.destroyCompleted();
    }

    // ...

    // 接收事件的改变
    @Subscribe
    public void onTodoStoreChange(TodoStore.TodoStoreChangeEvent event) {
        updateUI();
    }

    // 更新UI, 核心方法
    private void updateUI() {
        // 设置适配器数据, 每次更新TodoStore的状态
        mListAdapter.setItems(sTodoStore.getTodos());
        ...
    }
}

代码比较长, 只截取了一部分. 其中View的核心部分:
(1) 活动创建器(ActionsCreator), 负责响应用户交互事件, 如checkAll()等.
(2) 接收状态修改(onTodoStoreChange), 负责接收存储器(Store)修改完成的数据, 并更新页面(updateUI).


活动创建器

活动创建器(ActionsCreator): 主要负责根据响应事件的类型和数据, 发送相应的活动到调度器(Dispatch). 也可以发送网络请求(WebApi), 获取异步数据, 完成后再发送.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
    }

    // ...
}

活动在调度器(Dispatcher)中创建.
活动创造器(ActionsCreator), 第一个参数是类型, 其余参数是数据, 即配对的Key-Value.


调度器

调度器(Dispatcher), 是事件分发的中心, 使用事件总线(EventBus), 发送活动到存储器(Store). 存储器根据活动的类型和数据, 进行处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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), 发送至事件总线(EventBus), 由存储器(Store)负责处理.
(2) emitChange(): 把存储器(Store)处理完的状态和数据, 发送修改通知至事件总线(EventBus), 提示视图(View)进行更新页面(UpdateUI).


存储器

存储器(Store): 负责存储数据和状态, 接收事件总线(EventBus)上的修改通知, 根据类型, 修改数据和状态. 也可以使用数据库和本地存储.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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;
    }

    @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;
            // ...
        }
    }

    private void destroyCompleted() {
        Iterator<Todo> iter = mTodos.iterator();
        while (iter.hasNext()) {
            Todo todo = iter.next();
            if (todo.isComplete()) {
                iter.remove();
            }
        }
    }

    // ...

    @Override
    public StoreChangeEvent changeEvent() {
        return new TodoStoreChangeEvent();
    }
}

存储器(Store)的核心是onAction()方法, 获得事件总线(EventBus)的通知, 修改数据, 完成后调用emitStoreChange()方法, 通知视图(View)进行数据更新(UpdateUI).


整套的循环逻辑都已经完成, 清晰可见, 这就是架构的好处吧.

Flux: View -> Action -> Dispatcher -> Store -> View.

想更多的了解Flux架构, Facebook参考.

原文链接:http://www.apkbus.com/blog-719059-60337.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP