Android - Flux架构

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


1. View视图

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().


2. ActionsCreator事件生成器

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.


3. Dispatcher调度器

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接收负责处理.


4. Store状态

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!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-25 08:58:57

Android - Flux架构的相关文章

适用于Android的Flux架构

原文链接 : Flux Architecture on Android 译文出自 : 开发技术前线 www.devtf.cn.未经允许,不得转载! 译者 : Mr.Simple 找到一个好的应用架构对于Android来说并非易事,Goodle似乎并不那么关心这方面,因为他们并没有推荐一个合适的应用架构.但是对于应用来说一个良好的架构是非常重要的.不管你是否同意,每个应用都应该有一个架构.因此,你最好为你的应用设计一个架构,而不是任由它发展. ## 清晰的软件架构 现在比较流行的架构是Bob大叔在

Android APP架构心得

前言 从JavaEE转到Android开发也2年多了,开发的项目也有4,5个了(公司项目),其中有3个项目前期都是自己独立开发,从一开始的毫无架构到现在对如何架构也有一点心得,所以在此分享出来,大家一起交流 什么是架构 在我看来,软件架构绝对不只是框架的堆砌,看我看来,架构是为了方便软件维护.扩展.安全性.切入性(我也不知道有没有人提出过这个关键字,因为的确很少看见,简单来说我这里说的切入性就是指一个以前没有接触过这个项目的人,能快速加入到这个项目中,对项目进行维护.修改和扩展) 维护性 一个好

Android应用架构

Android开发生态圈的节奏非常之快.每周都会有新的工具诞生,类库的更新,博客的发表以及技术探讨.如果你外出度假一个月,当你回来的时候可能已经发布了新版本的Support Library或者Play Services 我与Ribot Team一起做Android应用已经超过三年了.这段时间,我们所构建的Android应用架构和技术也在不断地演变.本文将向您阐述我们的经验,错误以及架构变化背后的原因. 曾经的架构 追溯到2012年我们的代码库使用的是基本结构,那个时候我们没有使用任何第三方网络类

iOS和android系统架构对比

iOS和android系统架构对比 iOS是基于UNIX的,直接与底层硬件通信.系统底层.应用框架.应用软件都是采用C/C++或者Objective-C写成的,所以有很高的运行效率. android是基于Linux内核设计,在Linux内核上运行一个Java虚拟机,虚拟机再运行软件.就好似在一个系统上又套了一个系统,以此内存消耗大,运行效率的.

Android系统架构的简单描述

架构图如下: 1)英文版: 2)中文版: 由图可知:Android 系统架构从下到上分为 Linux内核层.中间件. 应用程序框架层和应用程序层. 1.Linux kernel 负责硬件的驱动程序.网络.电源.系统安全以及内存管理等功能. 2.中间件:核心库和运行时(libraries & Android runtime) 1)核心库 即c/c++函数库部分,大多数都是开放源代码的函数库,例如webkit(引擎),该函数库负责 android网页浏览器的运行,例如 标准的 c 函数库libc.o

Android技术19:Android系统架构

Android系统架构是学习Android最基本的知识点,经常在技术面试过程中涉及到有关问题,接下来对Android系统架构进行总结. Android官方文档提供资料显示:Android系统分为4个层次,从低到高依次为Linux kernel(Linux内核层),Libraries和Android运行时Runtime,Android Framework,Applications. 1.Applications应用层 该层主要是运行在Andorid系统上的应用程序,如Home,SMS,Contac

Google工程师解析Android系统架构

导读:Sans Serif是Google的一位工程师,近日发布了一篇博文非常清楚的描述了Android系统架构,中国移动通信研究院院长黄晓庆在新浪微博上推荐了该文,并认为文中对Android的介绍很好,如下是CSDN对文章的简单编译: Andriod是什么? 首先,就像Android开源和兼容性技术负责人Dan Morrill在Android开发手册兼容性部分所解释的,"Android并不是传统的Linux风格的一个规范或分发版本,也不是一系列可重用的组件集成,Android是一个用于连接设备的

【译】Android应用架构

Android开发生态圈的节奏非常之快.每周都会有新的工具诞生,类库的更新,博客的发表以及技术探讨.如果你外出度假一个月,当你回来的时候可能已经发布了新版本的Support Library或者Play Services 我与Ribot Team一起做Android应用已经超过三年了.这段时间,我们所构建的Android应用架构和技术也在不断地演变.本文将向您阐述我们的经验,错误以及架构变化背后的原因. 曾经的架构 追溯到2012年我们的代码库使用的是基本结构,那个时候我们没有使用任何第三方网络类

【Android系统】Android系统架构简介

概述 Android系统架构如下图: 从上往下一共是四层: 应用程序层.应用程序框架层.函数库与Android运行时层.Linux内核层. 下面分别介绍各层: 分层介绍 应用程序层 包含系列的Android核心应用程序,大部分的Android应用也属于这一层,如日历.闹钟.浏览器等.这一层属于用户. 应用程序框架层 这一层就是普通开发者需掌握的层,我们使用框架里的各个模块来开发应用,这些模块和各种服务都可以复用.理解了这一层,开发者就能更快更好的开发应用.下面介绍一下各个模块: 序号 模块名 用