Android学习之Flux架构入门

Flux 架构介绍

Flux 架构 被Facebook使用来构建他们的客户端web应用。跟Clean Architecture一样,它不是为移动应用设计的,但是它的特性和简单可以让我们很好的在安卓项目中采用。

Flux模式最大的特点是单向的数据流,它的UI状态更新模式继承了MVC模式的设计思想。Flux并不是具体的框架,而是一套处理UI问题的模式,Android Flux同样不是具体的框架,你不需要导入或者集成任何新的代码就可以使用,而你需要做的事情是了解这套思想、遵循这种开发模式,查看我们提供的Android代码示例,写自己的代码。

要理解Flux,有两个关键的特点

1、数据流总是单向的

一个单向的数据流 是 Flux 架构的核心,也是它简单易学的原因。就如下面讨论的,在进行应用测试的时候,它提供了非常大的帮助。

2、应用被分成三个主要部分:

. View: 应用的界面。这里创建响应用户操作的action

. Dispatcher: 中心枢纽,传递所有的action,负责把它们运达每个Store

. Store: 维护一个特定application domain的状态。它们根据当前状态响应action,执行业务逻辑,同时在完成的时候发出一个change事件。这个事件用于view更新其界面。

这三个部分都是通过Action来通信的:一个简单的基本对象,以类型来区分,包含了和操作相关的数据。

Flux Android 架构

在安卓开发中使用Flux设计规范的目的是建立一个在简单性与易扩展易测试之间都比较平衡的架构。

第一步是找到Flux元素和安卓app组件之间的映射。

其中两个元素非常容易找到与实现。

View: Activity o或者Fragment

Dispatcher: 一个事件总线( event bus),在我的例子中将使用Otto,但是其它任何实现都应该是ok的。

Actions

Actions也不复杂。它们的实现和POJO一样简单,有两个主要属性:

1、Type: 一个String,定义了事件的类型。

2、Data: 一个map,装载了本次操作。

Store是Flux理论中最难的部分。

Stores响应Dispatcher发出的Action,执行业务逻辑并发送change事件。Stores的唯一输出是这单一的事件:change。其它对Store内部状态感兴趣的组件必须监听这个事件,同时使用它获取需要的数据。最后,stores必须对外公开一个获取application状态的接口。这样,view元素可以查询Stores然后相应的更新UI。

这里通过一个简单的小demo来讲述整个流程。我们的界面上有一个Button和一个TextView,点击Button后让TextView显示出文字。常规的实现,直接在Activity中完成逻辑,MVP模式,在Presenter层来进行,对于Flux架构,我们要怎么实现呢。通过上图我们可以看到,View会产生Action,然后被Dispatcher进行调度,经过Store相应处理,将数据显示出来。

如何产生Action

首先要知道Action是什么样

public class Action {

  private final String type;
  private final HashMap<String, Object> data;

  public Action(String type, HashMap<String, Object> data) {
    this.type = type;
    this.data = data;
  }

  public static Builder type(String type) {
    return new Builder().with(type);
  }

  public String getType() {
    return type;
  }

  public HashMap getData() {
    return data;
  }

  public static class Builder {
    private String type;
    private HashMap<String, Object> data;

    Builder with(String type) {
      if(type == null) {
        throw new IllegalArgumentException("Type may not be null.");
      }
      this.type = type;
      this.data = new HashMap<>();
      return this;
    }

    public Builder bundle(String key, Object value) {
      if (key == null) {
        throw new IllegalArgumentException("Key may not be null.");
      }
      if(value == null) {
        throw new IllegalArgumentException("Value may not be null.");
      }
      data.put(key, value);
      return this;
    }

    public Action build() {
      if (TextUtils.isEmpty(type)) {
        throw new IllegalArgumentException("At least one key is required.");
      }
      return new Action(type, data);
    }
  }
}

每一个Action有两个属性,一个来标记Type,另一个字段来存储传送的数据,通过Map来存放。

对于Action Type,我们可以通过一个接口或者类来进行记录,将所有的类型保存在其中。方便我们的调用。

public interface ShowActions {
  String TODO_SHOW = "todo-show";
  String GET_TEXT = "get-text";
}

如何创建Action,定义一个类,专门用来根据我们可能会出现的各种View的事件,定义出来各种Action

public class ActionsCreator {

  private static ActionsCreator instance;

  final Dispatcher mDispatcher;

  ActionsCreator(Dispatcher dispatcher){
    mDispatcher = dispatcher;
  }

  public static ActionsCreator get(Dispatcher dispatcher) {
    if (instance == null) {
      instance = new ActionsCreator(dispatcher);
    }
    return instance;
  }

  public void create(String text) {
    mDispatcher.dispatch(ShowActions.TODO_SHOW, ShowActions.GET_TEXT, text);
  }

在我们准备用ActionsCreator来创建Action的时候,我们并没有直接new Action这种方式来做,而是将其通过调度器,对其进行了分发。这里的事件分发,我们使用的是OttoBus来进行事件的分发。

public class Dispatcher {

  private final Bus bus;
  private static Dispatcher instance;

  Dispatcher(Bus bus){
    this.bus = bus;
  }

  public static Dispatcher get(Bus bus) {
    if (instance == null) {
      instance = new Dispatcher(bus);
    }
    return instance;
  }

  public void register(final Object cls) {
    bus.register(cls);
  }

  public void unRegister(final Object cls) {
    bus.unregister(cls);
  }

  public void emitChange(Store.StoreChangeEvent o) {post(o);}

  public void dispatch(String type, Object... data) {
    if(TextUtils.isEmpty(type)) {
      throw new IllegalArgumentException("Type must not be empty");
    }

    if (data.length % 2 != 0) {
      throw new IllegalArgumentException("Data must be a valid list of key");
    }

    Action.Builder actionBuilder = Action.type(type);
    for (int i = 0; i < data.length; i++) {
      String key = (String) data[i++];
      Object value = data[i++];
      actionBuilder.bundle(key, value);
    }
    post(actionBuilder.build());
  }

  private boolean isEmpty(String type) {
    return TextUtils.isEmpty(type);
  }

  private void post(final Object event) {
    bus.post(event);
  }
}

在调度的过程中,我们将传递进来的数据进行一个解析,然后根据数据创建出相应的Action,然后对Action进行分发,这个时候关注了相应的ActionStore就会开始根据相应的Action开始执行相应的操作。在Store中,声明了一个抽象方法onAction来负责进行对于Action的判断和分发,然后定义了StoreChangeEvent接口作为事件变化,当有变化的时候,通过这个进行传递,我们可以自己实现这个接口,然后在里面添加一些方法和字段用来携带数据。

public abstract class Store {
  final Dispatcher mDispatcher;

  protected Store(Dispatcher dispatcher) {
    this.mDispatcher = dispatcher;
  }

  void emitStoreChange() {
    mDispatcher.emitChange(changeEvent());
  }

  abstract StoreChangeEvent changeEvent();

  public abstract void onAction(Action action);

  public interface StoreChangeEvent {}

}

我们自定义的Store类

public class ShowStore extends Store {

  private static ShowStore instance;
  private String showText;

  public ShowStore(Dispatcher dispatcher){
    super(dispatcher);
  }

  public static ShowStore get(Dispatcher dispatcher) {
    if (instance == null) {
      instance = new ShowStore(dispatcher);
    }
    return instance;
  }

  @Subscribe
  public void onAction(Action action) {
    switch (action.getType()) {
      case ShowActions.TODO_SHOW :
        showText = ((String)action.getData().get(ShowActions.GET_TEXT));
        Log.i("showText", showText);
        emitStoreChange();
        break;
      default:
        break;
    }
  }

  public String getShowText(){
    return showText;
  }

  @Override
  StoreChangeEvent changeEvent() {
    return new ShowChangeEvent();
  }

  public class ShowChangeEvent implements StoreChangeEvent {

  }
}

然后我们在View也就是Activity中订阅了变化时间的方法,这个时候就可以实现对于View中的数据的一个动态更新。

 @Subscribe
  public void showText (ShowStore.ShowChangeEvent event){
    mTextView.setText(mShowStore.getShowText());
   }

总结

通过Flux架构,使用的流程是,我们的View的事件会携带数据,通过一个ActionsCreate创建一个Type的Action,实际完成过程是在Dispatcher的dispatch中,然后再将这个Action丢给订阅了该Action的Store方法中,在这里完成各种逻辑,处理,甚至是可以发起网络请求获取数据,处理完成,可以将结果封装成一个事件,然后这个事件会再次通过调度器中的emitChangeEvent将事件传递给订阅了该事件的函数,而这个接收响应事件的函数被我们定义在我们View中,从而实现对于我们View的更新。以上就是本文的全部内容了,希望本文的内容对大家学习Flux架构有所帮助。

(0)

相关推荐

  • Android AndBase框架使用封装好的函数完成Http请求(三)

    本文是针对AndBase框架学习整理的第三篇笔记,想要了解AndBase框架的朋友可以阅读本文,大家共同学习. 学习内容: 1.使用AndBase框架实现无参Http Get请求... 2.使用AndBase框架实现有参Http Post请求... 3.使用AndBase框架实现有参Http Get请求... AndBase框架为我们提供了一些相关的方法提供给我们使用,用来完成Http网络请求...总体就是对Http请求的一个封装,不过个人认为,网络请求这一模块更加推荐使用Volley框架..楼

  • 简略分析Android的Retrofit应用开发框架源码

    面对一个项目,对于Android应用开发框架的选择,我想过三种方案: 1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和LoaderManager,代码健壮性强. 缺点是整套代码学习成本较高,使用过程中样板代码较多,(比如每一个Request都需要产生一个新类) 2.Volley,作为Google在IO大会上得瑟过的一个网络库,其实不算什么新东西(2013 IO发布),使用较为简单

  • Android通用流行框架大全【整理】

    Android通用流行框架大全 1. 缓存 DiskLruCacheJava实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader一个强大的加载,缓存,展示图片的库Picasso一个强大的图片下载与缓存的库Fresco一个用于管理图像和他们使用的内存的库Glide一个图片加载和缓存的库 3. 图片处理 Picasso-transformations一个为Picasso提供多种图片变换的库Glide-transformations一个为Glide提供多

  • Android Retrofit 2.0框架上传图片解决方案

    本文为大家分享了 Android Retrofit 2.0框架上传图片解决方案,具体内容如下 1.单张图片的上传 /** * 上传一张图片 * @param description * @param imgs * @return */ @Multipart @POST("/upload") Call<String> uploadImage(@Part("fileName") String description, @Part("file\&qu

  • Android最基本的异步网络请求框架

    本篇文章我们来一起写一个最基本的Android异步网络请求框架,借此来了解下Android中网络请求的相关知识.由于个人水平有限,文中难免存在疏忽和谬误,希望大家可以指出,谢谢大家. 1. 同步网络请求 以HTTP的GET请求为例,我们来介绍一下Android中一个基本的同步请求框架的实现.直接贴代码: public class HttpUtils { public static byte[] get(String urlString) { HttpURLConnection urlConnec

  • 六款值得推荐的android(安卓)开源框架简介

    1.volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)  JSON,图像等的异步下载: (2)  网络请求的排序(scheduling) (3)  网络请求的优先级处理 (4)  缓存 (5)  多级别取消请求 (6)  和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.android-async-http 项目地址:https://github.com/loopj/android-asyn

  • Android中XUtils3框架使用方法详解(一)

    xUtils简介 xUtils 包含了很多实用的android工具. xUtils 支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响... xUitls 最低兼容android 2.2 (api level 8) 今天给大家带来XUtils3的基本介绍,本文章的案例都是基于XUtils3的API语法进行的演示.相信大家对这个框架也都了解过, 下面简单介绍下XUtils3的一些基本知识. XUtils3一共有4大功能:注解模块,网络

  • Android之使用Android-query框架开发实战(二)

    在上篇文章跟大家介绍了Android之使用Android-query框架开发实战(一),本文继续跟大家介绍有关Android-query框架.具体内容请看下文. 异步网络: 1. 添加权限:<uses-permission android:name="android.permission.INTERNET" /> 2. 支持的类型 JSONObject JSONArray String (HTML, XML) XmlDom (XML parsing) XmlPullPars

  • 举例讲解Android应用开发中OTTO框架的基本使用

    OTTO是一个EventBus类型的事件传输总线,它可以提供"存储转发"的功能,让你APP中各个组件的交流更加便利,让你的程序分层更加清晰. 使用场景 OTTO基于Observer设计模式.它有发布者,订阅者这两个主要对象.OTTO的最佳实践就是通过反射牺牲了微小的性能,同时极大的提高了程序的耦合度,更加利于MVP分工开发与维护.业务层开发者在处理资源(比如Db, REST等)后并发布消息,展示层开发者(比如Activity/Fragment)就可以处理消息,而不用关心数据是怎么来的(

  • Android学习之Flux架构入门

    Flux 架构介绍 Flux 架构 被Facebook使用来构建他们的客户端web应用.跟Clean Architecture一样,它不是为移动应用设计的,但是它的特性和简单可以让我们很好的在安卓项目中采用. Flux模式最大的特点是单向的数据流,它的UI状态更新模式继承了MVC模式的设计思想.Flux并不是具体的框架,而是一套处理UI问题的模式,Android Flux同样不是具体的框架,你不需要导入或者集成任何新的代码就可以使用,而你需要做的事情是了解这套思想.遵循这种开发模式,查看我们提供

  • Android学习项目之简易版微信为例(二)

    1 概述 从这篇开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样,需要从main开始写代码逻辑,大部分逻辑控制代码都由自己来实现.事实上,Android已经为我们提供了一个程序运行的框架,我们只需要往框架中填入我们所需的内容即可,这里的内容主要是:四大组件--Activity.Service.ContentProvider.BroadCast.在这四大组件中,可以实现

  • Android学习项目之简易版微信为例(一)

    这是"Android学习之路"系列文章的开篇,可能会让大家有些失望--这篇文章中我们不介绍简易版微信的实现(不过不是标题党哦,我会在后续文章中一步步实现这个应用程序的).这里主要是和广大朋友们聊聊一个非Java程序员对Android操作系统的理解以及一个Android工程的目录结构,为进一步学习做准备. 1 缘起 智能手机的出现与普及为人们的生活.工作带来了极大的便利,我们可以用手机随时随地.随心所欲地购物.玩游戏.聊天.听音乐等等.一个个精心设计.体验良好的移动客户端应用,让用户们爱

  • Android学习之Intent中显示意图和隐式意图的用法实例分析

    本文实例讲述了Android学习之Intent中显示意图和隐式意图的用法.分享给大家供大家参考,具体如下: Intent(意图)主要是解决Android应用的各项组件之间的通讯. Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用. 因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦. 例如,在一个联系人

  • Android学习笔记(二)App工程文件分析

    App工程文件分析 关于如何创建一个最简单的Android App请参照链接: < Android学习笔记(一)环境安装及第一个hello world > http://www.jb51.net/article/52593.htm 创建完的工程文件如下图所示,本文对一些主要的文件进行分析. src文件分析 App源文件如图: 打开源文件 MainActivity.java 可看到如下代码: 源码主要功能如下: App源文件目录 package com.example.firstapp; 导入A

  • Android学习之Broadcast的简单使用

    本文实例为大家分享了Android学习之Broadcast的使用方法,供大家参考,具体内容如下 实现开机启动提示网络的广播 package com.example.luobo.broadcasttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; impor

  • Android学习笔记45之gson解析json

    JSON即JavaScript Object Natation, 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种理想的数据交换格式. JSON对象: JSON中对象(Object)以"{"开始, 以"}"结束. 对象中的每一个item都是一个key-value对, 表现为"key:value"的形式, key-value对之间使用逗号分隔. 如:{"name":"coolxing

  • Android学习笔记之应用单元测试实例分析

    本文实例讲述了Android学习笔记之应用单元测试.分享给大家供大家参考,具体如下: 第一步:在AndroidManifest.xml中加入如下两段代码: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.pccw" android:versionCode="1" android:versionName="1.0&qu

  • MyBatis入门学习教程-MyBatis快速入门

    目录 Mybatis 一.快速开始 1.创建 Maven 项目 2.导入 Maven 依赖 3.配置 Maven 插件 4.新建数据库,导入表格 5.编写 Mybatis 配置文件 6.编写实体类 7.编写 mapper 接口 8.编写 mapper 实现 9.Mybatis 配置文件中,添加 mapper 映射 10.编写 Mybatis 工具类 11.测试 二.日志添加 1.添加 Maven 依赖 2.添加 log4j 配置 3.Mybatis 中配置 LOG 4.执行测试 三.Mybati

  • Android学习之Span的使用方法详解

    目录 Span集合 段落类Span 其他Span 展示效果 小试牛刀 小结 Span集合 段落类Span BulletSpan 为段落开头增加项目符号并支持大小.颜色.弧度 span.append(SpannableString("BulletSpan").also { it.setSpan(BulletSpan(40, Color.RED), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }) QuoteSpan 为段落开头增加垂直引用线 sp

随机推荐