[译]Android Flux 框架 RxFlux 介绍

原文

简介

RxFlux 是一个使用 RxJava1 实现 Flux模式 的轻量级框架,RxFlux 仅仅提供 Flux 模式的一种实现,它需要手动集成,并且需要开发者遵守 Flux 步骤才可以正常工作。

优势: 架构清晰、步骤简单、逻辑抽象、容易测试、模块化、响应式、添加 Hooks 容易。
不足: 线性逻辑、不同的模式,使用前需要学习。

Wiki-1:Getting started

RxFlux 是一个框架,意味着需要做一些准备工作,而不仅仅是作为一个 library。使用 RxFlux 框架最好了解 RxJava 和 Flux。如果你需要更多的知识来理解 Android 中的 Flux 模式,可以查看@lgvalle 创建的例子和说明

记住 Flux 的框架能更好的理解 RxFlux。

Flux architecture

第一步添加 RxFlux 到 build.gradle 文件中:

dependencies {
  compile 'com.hardsoftstudio:rxflux:latest'
}
public void onCreate() {
    super.onCreate();
    rxFlux = RxFlux.init();
}

Wiki-2:Setup Action

创建接口 Actions 包含你的 actions:

public interface Actions {

  String GET_PUBLIC_REPOS = "get_public_repos";
  void getPublicRepositories();

  String GET_USER = "get_user";
  void getUserDetails(String userId);
}

创建一个 RxActionCreator 类将创建 RxAction 根据前面的接口 Actions:

public class GitHubActionCreator extends RxActionCreator implements Actions { 

  public GitHubActionCreator(Dispatcher dispatcher, SubscriptionManager manager) {
    super(dispatcher, manager);
  }

  //实现定义的actions
}

大部分的 actions 是请求一个 API,DB 或你想执行的操作。因此创建 RxAction 时,您必须返回这些请求的结果。推荐创建一个接口 Keys,将帮助我们整理数据。

public interface Keys {
  String PUBLIC_REPOS = "repos";
  String USER = "user";
  String ID = "id";
}

创建第一个 action:

  @Override
  public void getUserDetails(String userId) {
    final RxAction action = newRxAction(GET_USER, Keys.ID, userId);
    ...
  }

方法 newRxAction()帮助你创建一个新的 RxAction。传入参数 action type 和 key-value。

现在我们有一个 RxAction,我们可以请求 API 或处理一些逻辑来获取所需的 action 的结果。

  @Override
  public void getUserDetails(String userId) {
    final RxAction action = newRxAction(GET_USER, Keys.ID, userId);
    addRxAction(action, NetworkManager.getApi()
        .getUser(userId)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(user -> {
          action.getData().put(USER, user);
          postRxAction(action);
        }, throwable -> postError(action, throwable)));
  }

这里我们使用 RxJava,我们创建一个新的 Observable 并执行 subscribe。产生的 Subscription 被添加到 SubscriptionManager。

当这个 Subscription 返回一个有效的数据时,我们用 key-value 加入到 RxAction。然后我们使用预定义的方法 postRxAction() 发送 action 到 Dispatcher。

在出错的情况下,我们将使用预定义的方法 postError() 发送一个 error 到 Dispatcher。

为避免重复的请求,当你添加一个 Subscription 到 SubscriptionManager,这个 Subscription 将保存直到它被 unsubscribed。使用 hasRxAction(RxAction)方法来检查是否有一个正在进行 Subscription。

  @Override
  public void getUserDetails(String userId) {
    final RxAction action = newRxAction(GET_USER, Keys.ID, userId);
    if (hasRxAction(action)) return; // Return or cancel previous 
    ...
   }

Wiki-3:Setup Store

第一步定义 store,创建接口。

public interface RepositoriesStoreInterface {

  ArrayList<GitHubRepo> getRepositories();

}

创建一个 store 继承 RxStore。

public class RepositoriesStore extends RxStore implements RepositoriesStoreInterface {
    ...
}

RxStore 是一个抽象类,同 Dispatcher 一起处理 subscription 来接收 actions。为接收这些 actions,我们需要实现 onRxAction()

public class RepositoriesStore extends RxStore implements RepositoriesStoreInterface {

  public RepositoriesStore(Dispatcher Dispatcher) {
    super(Dispatcher);
  }

  @Override
  public void onRxAction(RxAction action) {

  }
}

构造函数需要 Dispatcher 作为参数,以便订阅它。onRxAction() 方法将得到Dispatcher 发布的所有 actions。使用 switch 语句来过滤这些 actions。

  @Override
  public void onRxAction(RxAction action) {
    switch (action.getType()) {
      case Actions.GET_PUBLIC_REPOS:
        this.gitHubRepos = action.get(Keys.PUBLIC_REPOS);
        break;
      default: // IMPORTANT 不需要处理的action,被忽略
        return;
    }
    postChange(new RxStoreChange(ID, action));
  }

因为 store 将接收所有类型的 actions,而我们只处理那些我们关心的。为了做到这一点,我们检索 actions 中数据并处理,然后发布一个 RxStoreChange,通知 views。使用方法 postChange() 发布 RxStoreChange。这种方法发布的RxStoreChange 包含将一个给定的 store id (主要用于标识那个 store 发出的)和 RxAction。

最后一步是实现公共接口,提供的我们想要的数据。

  @Override
  public ArrayList<GitHubRepo> getRepositories() {
    return gitHubRepos == null ? new ArrayList<GitHubRepo>() : gitHubRepos;
  }

views 可以在接收到一个 RxStoreChange 时或者任何需要的时候,调用该方法,得到 store 中的数据。

Wiki-4:Setup View

views 负责触发 actions,注册 stores 并使用 stores 的数据。

第一步是项目中的每个 activity 必须实现 RxViewDispatch。

getRxStoreListToRegister() 告诉 RxFlux 哪个 store 需要注册。

  @Override
  public List<RxStore> getRxStoreListToRegister() {
    repositoriesStore = RepositoriesStore.get(SampleApp.get(this).getRxFlux().getDispatcher());
    usersStore = UsersStore.get(SampleApp.get(this).getRxFlux().getDispatcher());
    return Arrays.asList(repositoriesStore, usersStore);
  }

我们如何建立我们的商店。在这个例子中,我们得到的需要注册的 store 的实例。RxFlux 在 activity 创建成功之后会调用该方法,若 activity 是 RxViewDispatch 的子类,获取需要注册的 RxStoreList 并调用 RxStore 中的方法 register(),注册 store 到 Dispatcher 中。

Note: 为了安全,在多次调用注册时,我们只注册一次。

onRxStoreChanged() 在每次 store 发送一个 RxStoreChange 的时候将被调用。

  @Override
  public void onRxStoreChanged(RxStoreChange change) {
    switch (change.getStoreId()) {
      case RepositoriesStore.ID:
        switch (change.getRxAction().getType()) {
          case Actions.GET_PUBLIC_REPOS:
            adapter.setRepos(repositoriesStore.getRepositories());
            break;
        }
        break;
    }
  }

RxStoreChange 有一个 store 的标识和一个 RxAction。通过这两个,views 可以执行期望的逻辑。

onRxError() 在 RxError 被 post 到 Dispatcher 时调用。

  @Override
  public void onRxError(RxError error) {
    setLoadingFrame(false);
    Throwable throwable = error.getThrowable();
    if (throwable != null) {
      Snackbar.make(coordinatorLayout, "An error ocurred", Snackbar.LENGTH_INDEFINITE)
          .setAction("Retry",
              v -> SampleApp.get(this).getGitHubActionCreator().retry(error.getAction()))
          .show();
    } else {
      Toast.makeText(this, "Unknown error", Toast.LENGTH_LONG).show();
    }
  }

这种方法允许我们作出反应,当我们得到一个错误时。注意 RxError 包含期望的 RxAction。因此,我们可以使用它来重试或显示适当的信息。

RxViewDispatcher 的最后两个方法是用来注册其他非 activity 的 view(fragments,customViews等)。


  @Override
  public void onRxViewRegistered() {
    // If there is any fragment that needs to register store changes we can do it here
  }

  @Override
  public void onRxViewUnRegistered() {
    // If there is any fragment that has registered for store changes we can unregister now
  }

高级选项

RxFlux 将管理应用程序生命周期,在正确的时候注册和注销 views,避免内存泄漏。如果应用程序被destroyed,还将负责清理任何订阅。

有关更多信息,请查看 RxFlux.class (主类,负责连接每个部分,基于 Android 的生命周期)。

Flow

  1. RxFlux 会在 activity 创建时,注册我们所需要的 store,调用
    getRxStoreListToRegister()
  2. 在 activity resume 时,RxFlux 将注册这个 activity 到 Dispatcher 中。
  3. 在 activity pause 时,RxFlux 从 Dispatcher 中解除这个 activity 的注册。
  4. 当最后一个 activity 被 destroy 时,RxFlux 将调用关闭。

为什么在 onResume() 注册和在 onPause() 注销?

根据 activity 的生命周期来处理注册和解除注册,这是正确的方法,可以避免当 view 不在前台的时候,获取 RxStoreChange 并响应触发 UI 变化。
 
一个良好的实践是在 onResume() 期间,检查 store 的状态并更新 UI。

公共方法

  public static RxFlux init(Application application) {
    if (instance != null) throw new IllegalStateException("Init was already called");
    return instance = new RxFlux(application);
  }

初始化 RxFlux 框架,必须在 Application 创建时调用此方法,并只有一次。

  public static void shutdown() {
    if (instance == null) return;
    instance.subscriptionManager.clear();
    instance.Dispatcher.unregisterAll();
  }

调用这个方法停止应用程序,清理所有的订阅和注册。

  public RxBus getRxBus() {
    return rxBus;
  }

RxBus 的实例,如果您想在您的应用程序中重用作为默认的 bus system。

  public Dispatcher getDispatcher() {
    return Dispatcher;
  }

Dispatcher 的实例,负责处理订阅,传送 bus event 到正确的位置。在创建 RxActionCreator 时,使用这个实例。

  public SubscriptionManager getSubscriptionManager() {
    return subscriptionManager;
  }

SubscriptionManager 的实例,以便你想要重用的别的东西。在创建 RxActionCreator 时,使用这个实例。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容