热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何使用Java(非Kotlin)在Android中使用RxJava管理状态

如何解决《如何使用Java(非Kotlin)在Android中使用RxJava管理状态》经验,请帮忙看看怎么搞?

我试图根据杰克沃顿提出的以下谈话开发一个Android应用程序

The State of Managing State with RxJava
21 March 2017 – Devoxx (San Jose, CA, USA)

杰克答应了我无法找到的第2部分和/或GITHUB示例(如果确实存在)

在高层次上,我可以关注/理解上述谈话的大部分内容.

但是我有以下问题.

我可以看到如何使用UiEvent,UiModel,Action和Result来保持关注点.

令我困惑的是以下几点: -

幻灯片194上的图表显示了Observables的"流/流"

Android Device -----> Observable ----->  -----> Observable  -----> {Backend}
{Backend}      -----> Observable  ----->  -----> Observable -----> Android Device

幻灯片210包含此代码片段,显示结果流如何"扫描"到UiModel中

SubmitUiModel initialState = SubmitUiModel.idle();
Observable results = /* ... */;
Observable uiModels = results.scan(initialState, (state, result) -> {
if (result == CheckNameResult.IN_FLIGHT
|| result == SubmitResult.IN_FLIGHT)
return SubmitUiModel.inProgress();
if (result == CheckNameResult.SUCCESS)
return SubmitUiModel.idle();
if (result == SubmitResult.SUCCESS)
return SubmitUiModel.success();
// TODO handle check name and submit failures...
throw new IllegalArgumentException("Unknown result: " + result);
});

和幻灯片215上的最终代码片段,代码片段类似于: -

ObservableTransformer submit =
actions -> actions.flatMap(action -> service.setName(action.name)
.map(response -> SubmitResult.SUCCESS)
.onErrorReturn(t -> SubmitResult.failure(t.getMessage()))
.observeOn(AndroidSchedulers.mainThread())
.startWith(SubmitResult.IN_FLIGHT));

ObservableTransformer checkName =
actions -> actions.switchMap(action -> action
.delay(200, MILLISECONDS, AndroidSchedulers.mainThread())
.flatMap(action -> service.checkName(action.name))
.map(response -> CheckNameResult.SUCCESS)
.onErrorReturn(t -> CheckNameResult.failure(t.getMessage()))
.observeOn(AndroidSchedulers.mainThread())
.startWith(CheckNameResult.IN_FLIGHT));

这说明从Action(s)到Result(s)的转换

关于如何将UiEvent/UiModel与Action/Result流相结合,我在这个谈话/幻灯片中缺少什么?

流由UiEvents驱动如何完成从UiEvent到Action再到Result然后最终UiModel的流程?

更新 使用星球大战API我采取了以下方法,我使用我的UI事件来驱动UI事件到结果之间通过动作的转换,然后扫描结果以映射回UI模型.

继承我的课程和代码: -

ACTION CLASSES
==============

public abstract class Action {

    Api service = Service.instance();

    final T data;

    public Action(final T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public abstract Observable> execute();
}


public class CheckCharacterAction extends Action {

    public CheckCharacterAction(final String characterName) {
        super(characterName);
    }

    @Override
    public Observable> execute() {
        return service.peopleSearch(getData());
    }    
}

public class CheckFilmAction extends Action {    
    public CheckFilmAction(final String filmTitle) {
        super(filmTitle);
    }

    @Override
    public Observable> execute() {
        return service.filmSearch(getData());
    }    
}

public class SearchAction extends Action {    
    public SearchAction(final String search) {
        super(search);
    }

    @Override
    public Observable>  execute() {
        return service.filmSearch(getData());
    }    
}

EVENT CLASSES
=============
public abstract class UiEvent {

    private final T data;

    public UiEvent(final T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

public class CharacterUiEvent extends UiEvent {
    public CharacterUiEvent(final String name) {
        super(name);
    }
}

public class FilmUiEvent extends UiEvent {
    public FilmUiEvent(final String title) {
        super(title);
    }
}

public class SearchUiEvent extends UiEvent {
    public SearchUiEvent(final String data) {
        super(data);
    }
}

UI MODEL CLASSES
================
public class UiModel {

    public final boolean isProgress;
    public final String message;
    public final boolean isSuccess;
    public T data;

    public UiModel(final boolean isProgress) {
        this.isProgress = isProgress;
        this.message = null;
        this.isSuccess = false;
        this.data = null;
    }

    public UiModel(final T data) {
        this.isProgress = false;
        this.message = null;
        this.isSuccess = true;
        this.data = data;
    }

    public UiModel(final String message) {
        this.isProgress = false;
        this.message = message;
        this.isSuccess = false;
        this.data = null;
    }

    public UiModel(final boolean isProgress, final String message, final boolean isSuccess, final T data) {
        this.isProgress = isProgress;
        this.message = message;
        this.isSuccess = isSuccess;
        this.data = data;
    }
}

public class CharacterUiModel extends UiModel {

    public CharacterUiModel(final boolean isProgress) {
        super(isProgress);
    }

    public CharacterUiModel(final JsonData data) {
        super(data);
    }

    public CharacterUiModel(final String message) {
        super(message);
    }

    public CharacterUiModel(final boolean isProgress, final String message, final boolean isSuccess, final JsonData data) {
        super(isProgress, message, isSuccess, data);
    }


    public static CharacterUiModel inProgress() {
        return new CharacterUiModel(true);
    }

    public static CharacterUiModel success(final JsonData data) {
        return new CharacterUiModel(data);
    }

    public static CharacterUiModel failure(final String message) {
        return new CharacterUiModel(message);
    }

}

public class FilmUiModel extends UiModel {


    public FilmUiModel(final boolean isProgress) {
        super(isProgress);
    }

    public FilmUiModel(final JsonData data) {
        super(data);
    }

    public FilmUiModel(final String message) {
        super(message);
    }

    public FilmUiModel(final boolean isProgress, final String message, final boolean isSuccess, final JsonData data) {
        super(isProgress, message, isSuccess, data);
    }


    public static FilmUiModel inProgress() {
        return new FilmUiModel(true);
    }

    public static FilmUiModel success(final JsonData data) {
        return new FilmUiModel(data);
    }

    public static FilmUiModel failure(final String message) {
        return new FilmUiModel(message);
    }

}

public class SearchUiModel extends UiModel {

    private SearchUiModel(final boolean isProgress) {
        super(isProgress);
    }

    private SearchUiModel(final JsonData data) {
        super(data);
    }

    private SearchUiModel(final String message) {
        super(message);
    }

    private SearchUiModel(final boolean isProgress, final String message, final boolean isSuccess, final JsonData data) {
        super(isProgress, message, isSuccess, data);
    }

    public static SearchUiModel idle() {
        return new SearchUiModel(false, null, false, null);
    }

    public static SearchUiModel inProgress() {
        return new SearchUiModel(true);
    }

    public static SearchUiModel success(final JsonData data) {
        return new SearchUiModel(data);
    }

    public static SearchUiModel failure(final String message) {
        return new SearchUiModel(message);
    }
}


RESULT CLASSES
==============

public abstract class Result {

    public enum LIFECYCLE {
        DEPARTURE_LOUNGE,
        IN_FLIGHT,
        LANDED_SAFELY,
        CRASHED_BURNED
    }

    final LIFECYCLE lifecycle;
    final T data;
    final String errorMessage;

    public Result(final LIFECYCLE lifecycle, final T data, final String errorMessage) {
        this.lifecycle = lifecycle;
        this.data = data;
        this.errorMessage = errorMessage;
    }

    public T getData() {
        return data;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public LIFECYCLE getLifecycle() {
        return lifecycle;
    }
}

public class CharacterResult extends Result {

    private CharacterResult(final LIFECYCLE lifecycle, final JsonData data, final String errorMessage) {
        super(lifecycle, data, errorMessage);
    }

    private CharacterResult(final LIFECYCLE lifecycle) {
        super(lifecycle, null, null);
    }

    public static CharacterResult departureLounge() {
        return new CharacterResult(LIFECYCLE.DEPARTURE_LOUNGE);
    }

    public static CharacterResult inflight() {
        return new CharacterResult(LIFECYCLE.IN_FLIGHT);
    }

    public static CharacterResult landedSafely(final JsonData data) {
        return new CharacterResult(LIFECYCLE.LANDED_SAFELY, data, null);
    }

    public static CharacterResult crashedBurned(final String errorMessage) {
        return new CharacterResult(LIFECYCLE.CRASHED_BURNED, null, errorMessage);
    }
}


public class FilmResult extends Result {

    private FilmResult(final LIFECYCLE lifecycle, final JsonData data, final String errorMessage) {
        super(lifecycle, data, errorMessage);
    }

    private FilmResult(final LIFECYCLE lifecycle) {
        super(lifecycle, null, null);
    }

    public static FilmResult departureLounge() {
        return new FilmResult(LIFECYCLE.DEPARTURE_LOUNGE);
    }

    public static FilmResult inflight() {
        return new FilmResult(LIFECYCLE.IN_FLIGHT);
    }

    public static FilmResult landedSafely(final JsonData data) {
        return new FilmResult(LIFECYCLE.LANDED_SAFELY, data, null);
    }

    public static FilmResult crashedBurned(final String errorMessage) {
        return new FilmResult(LIFECYCLE.CRASHED_BURNED, null, errorMessage);
    }
}

public class SearchResult extends Result {

    private SearchResult(final LIFECYCLE lifecycle, final JsonData data, final String errorMessage) {
        super(lifecycle, data, errorMessage);
    }

    private SearchResult(final LIFECYCLE lifecycle) {
        super(lifecycle, null, null);
    }

    public static SearchResult departureLounge() {
        return new SearchResult(LIFECYCLE.DEPARTURE_LOUNGE);
    }

    public static SearchResult inflight() {
        return new SearchResult(LIFECYCLE.IN_FLIGHT);
    }

    public static SearchResult landedSafely(final JsonData data) {
        return new SearchResult(LIFECYCLE.LANDED_SAFELY, data, null);
    }

    public static SearchResult crashedBurned(final String errorMessage) {
        return new SearchResult(LIFECYCLE.CRASHED_BURNED, null, errorMessage);
    }
}

然后我从我的Activity onCreate()方法中设置我的Rx Streams如下: -

   final Observable searchEvents = RxView.clicks(activityMainBinding.searchButton)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.mainThread())
            .map(ignored -> new SearchUiEvent(activityMainBinding.filmTitle.getText().toString()));

    final Observable filmEvents = RxTextView.afterTextChangeEvents(activityMainBinding.filmTitle)
            .skipInitialValue()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.mainThread())
            .delay(1000, MILLISECONDS, AndroidSchedulers.mainThread())
            .map(text -> new FilmUiEvent(text.view().getText().toString()));

    final Observable characterEvents = RxTextView.afterTextChangeEvents(activityMainBinding.people)
            .skipInitialValue()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.mainThread())
            .delay(200, MILLISECONDS, AndroidSchedulers.mainThread())
            .map(text -> new CharacterUiEvent(text.view().getText().toString()));

    /**
     *
     */
    final Observable uiEvents = Observable.merge(searchEvents, filmEvents, characterEvents);

    /*********
     *
     */

    final ObservableTransformer searchAction =
            events -> events.flatMap(event -> new SearchAction(event.getData()).execute().subscribeOn(Schedulers.io()))
                    .map(response -> SearchResult.landedSafely(new JsonData(response.body())))
                    .onErrorReturn(throwable -> SearchResult.crashedBurned(throwable.getMessage()))
                    .startWith(SearchResult.inflight());

    final ObservableTransformer filmAction =
            events -> events.flatMap(event -> new CheckFilmAction(event.getData()).execute().subscribeOn(Schedulers.io()))
                    .map(response -> FilmResult.landedSafely(new JsonData(response.body())))
                    .onErrorReturn(throwable -> FilmResult.crashedBurned(throwable.getMessage()))
                    .startWith(FilmResult.inflight());

    final ObservableTransformer characterAction =
            events -> events.flatMap(event -> new CheckCharacterAction(event.getData()).execute().subscribeOn(Schedulers.io()))
                    .map(response -> CharacterResult.landedSafely(new JsonData(response.body())))
                    .onErrorReturn(throwable -> CharacterResult.crashedBurned(throwable.getMessage()))
                    .startWith(CharacterResult.inflight());

    final ObservableTransformer whatever = events -> events.publish(shared -> Observable.merge(
            shared.ofType(SearchUiEvent.class).compose(searchAction),
            shared.ofType(CharacterUiEvent.class).compose(characterAction),
            shared.ofType(FilmUiEvent.class).compose(filmAction)));

    /**
     *
     */
    final UiModel initialState = SearchUiModel.idle();

    final Observable results = uiEvents.compose(whatever).doOnSubscribe(COMPOSITE_DISPOSABLE::add);

    final Observable models = results.scan(initialState, (state, result) -> {
        Log.e(TAG, "scan() state = " + state + " result = " + result);
        if (result.getLifecycle().equals(SearchResult.LIFECYCLE.DEPARTURE_LOUNGE) ||
                result.getLifecycle().equals(CharacterResult.LIFECYCLE.DEPARTURE_LOUNGE) ||
                result.getLifecycle().equals(FilmResult.LIFECYCLE.DEPARTURE_LOUNGE)) {
            return SearchUiModel.idle();
        }

        if (result.getLifecycle().equals(SearchResult.LIFECYCLE.IN_FLIGHT) ||
                result.getLifecycle().equals(CharacterResult.LIFECYCLE.IN_FLIGHT) ||
                result.getLifecycle().equals(FilmResult.LIFECYCLE.IN_FLIGHT)) {
            return SearchUiModel.inProgress();
        }

        if (result.getLifecycle().equals(SearchResult.LIFECYCLE.LANDED_SAFELY) ||
                result.getLifecycle().equals(CharacterResult.LIFECYCLE.LANDED_SAFELY) ||
                result.getLifecycle().equals(FilmResult.LIFECYCLE.LANDED_SAFELY)) {
            return SearchUiModel.success((JsonData) result.getData());
        }

        if (result.getLifecycle().equals(SearchResult.LIFECYCLE.CRASHED_BURNED) ||
                result.getLifecycle().equals(CharacterResult.LIFECYCLE.CRASHED_BURNED) ||
                result.getLifecycle().equals(FilmResult.LIFECYCLE.CRASHED_BURNED)) {
            return SearchUiModel.failure(result.getErrorMessage());
        }


        return null;

    });

    models.doOnSubscribe(COMPOSITE_DISPOSABLE::add).subscribe(model -> report(model), throwable -> error(throwable));

一旦我的活动显示,我得到以下日志: -

2018-10-09 14:22:33.310 D/MainActivity: report() called with: model = [UiModel{isProgress=false, message='null', isSuccess=false, data=null}]
2018-10-09 14:22:33.311 E/MainActivity: scan() state = UiModel{isProgress=false, message='null', isSuccess=false, data=null} result = SearchResult{lifecycle=IN_FLIGHT, data=null, errorMessage='null'}
2018-10-09 14:22:33.311 D/MainActivity: report() called with: model = [UiModel{isProgress=true, message='null', isSuccess=false, data=null}]
2018-10-09 14:22:33.313 E/MainActivity: scan() state = UiModel{isProgress=true, message='null', isSuccess=false, data=null} result = CharacterResult{lifecycle=IN_FLIGHT, data=null, errorMessage='null'}
2018-10-09 14:22:33.313 D/MainActivity: report() called with: model = [UiModel{isProgress=true, message='null', isSuccess=false, data=null}]
2018-10-09 14:22:33.313 E/MainActivity: scan() state = UiModel{isProgress=true, message='null', isSuccess=false, data=null} result = FilmResult{lifecycle=IN_FLIGHT, data=null, errorMessage='null'}
2018-10-09 14:22:33.313 D/MainActivity: report() called with: model = [UiModel{isProgress=true, message='null', isSuccess=false, data=null}]

我猜我得到了这些IN FLIGHT结果,因为我的.startWith()陈述.

当我点击我的搜索按钮或在我的EditText视图中输入任何文本时,我会看到以下日志: -

2018-10-09 14:55:19.463 E/MainActivity: scan() state = UiModel{isProgress=false, message='null', isSuccess=true, data=com.test.model.JsonData@5e0b6f1} result = FilmResult{lifecycle=LANDED_SAFELY, data=com.test.model.JsonData@8ae4d86, errorMessage='null'}
2018-10-09 14:55:19.463 D/MainActivity: report() called with: model = [UiModel{isProgress=false, message='null', isSuccess=true, data=com.test.model.JsonData@8ae4d86}]

为什么我看不到"飞行中"然后"登陆安全"?

我只得到"安全着陆"

我的方法是在UI事件 - >动作 - >结果 - > UI模型之间进行转换,接近J沃顿先生描述的内容吗?

我哪里出错了?

更新(二)

我的错误是不在操作中包含我的所有下游Rx .flatmap().

澄清

这种UI事件模式--->动作--->结果---> UI模型是否仍然适用于没有"后端"的情况?例如,主屏幕可以向用户呈现许多选项(按钮)以导航到应用程序内的较低级别屏幕.UI事件将是"按钮单击",UI模型将返回关联的Activity类以与startActivity()方法调用一起使用.

How can I amalgamate the UI input events of a login screen into a single stream of UI events where I have two EditText fields (User Name and Password) and a Login Button. I would want the button click UI event to contain the user name and user password entered. If I was using RxBinding to process the EditTexts and the Login button click I cannot see how I can combine these three Observables into my UI event stream and have the EditTexts validated to ensure they have data entered and then pass this user entered data to my back end login API (or maybe Google Sign In for example)


推荐阅读
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
author-avatar
w3a00048_304
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有