RxResult:将异步调用转成同步风格的一个工具

1. 场景

为了更高效地利用多核处理器,采用异步多线程编程是一种常见手段,但是习惯于同步编程的开发者在尝试异步编程的时候可能会出现诸多不便,比如程序返回值的处理。

先来看一个简单的同步编程的示例:

private void testSync() throws InterruptedException {
    int result = doSomethingSync();
    //1. 可能不急着要用结果
    Thread.sleep(1000);//2. 模拟去干其他事情
    //3. 要用打时候直接取result的值就行
}

private int doSomethingSync(){
    try {
        Thread.sleep(3000);//模拟耗时操作
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return 1;//直接返回结果
}

这里的代码同步执行,testSync方法只有一条时间线顺序执行,在3处可以自然地读取1处的结果。但是转换成异步程序的时候,情况就不同了:

private void testAsync() throws InterruptedException {
    doSomethingAsync((Integer result) -> {
        //0. 处理调用结果
    });
    //1. 可能不急着需要结果
    Thread.sleep(1000);//2. 模拟去干其他事情
    //3. 如果后面想要拿到doSomethingAsync的结果,就得依赖其他手段
}
private void doSomethingAsync(Consumer<Integer> callback) {
    new Thread(() -> {
        try {
            Thread.sleep(3000);//模拟其他线程耗时操作
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        callback.accept(1);//返回结果给回调
    }).start();
}

代码执行到1处就出现了两条时间线,代码分叉了,想要处理doSomethingAsync的结果就得在回调中编写处理代码。如果想在3处来处理doSomethingAsync的结果,就比较麻烦。

本文就介绍一个利用RxJava打造的50行左右的工具来处理这种麻烦(RxResult的完整代码见最下方)。

2. 示例

接下来我们看几个示例。首先将我们原来的异步代码改为:

private RxResult<Integer> doSomethingWithRxResult() {
    RxResult<Integer> rxResult = new RxResult<>();
    new Thread(() -> {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        rxResult.postData(1);
    }).start();
    return rxResult;
}

这样调用doSomethingWithRxResult的代码就变成了:

private void testRxResult() throws InterruptedException {
    RxResult<Integer> rxResult = doSomethingWithRxResult();
    //1. 可能不急着需要结果
    Thread.sleep(1000);//2. 模拟去干其他事情

    //3. 如果想要以同步的方式取得结果,就编写为:
    Integer result = rxResult.getSingle().blockingGet();
    //4. 建议使用订阅的方式:
    rxResult.getSingle().subscribe((Integer r) -> {
        System.out.println("result:" + r);
    });
}

我们还可以将得到的RxResult到处传递、到处订阅取值:

private void testRxResult2() throws InterruptedException {
    RxResult<Integer> rxResult = doSomethingWithRxResult();
    //1. 可能不急着需要结果
    Thread.sleep(1000);//2. 模拟去干其他事情
    //4. 建议使用订阅的方式:
    rxResult.getSingle().subscribe((Integer r) -> {
        System.out.println("result:" + r);
    });

    delayHandleResult(rxResult);
}

private void delayHandleResult(RxResult<Integer> rxResult) {
    new Thread(() -> {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        rxResult.getSingle()
                .subscribe((Integer r) -> {
                    System.out.println("result2:" + r);
                });
    }).start();
}

我们还可以利用RxJava的zip方法来将多个异步调用的结果RxResult组合起来一起处理:

private void testRxResult3() {
    RxResult<Integer> rxResult1 = doSomethingWithRxResult();
    RxResult<Integer> rxResult2 = doSomethingWithRxResult();
    RxResult<Integer> rxResult3 = doSomethingWithRxResult();

    Single.zip(rxResult1.getSingle(), rxResult2.getSingle(), rxResult3.getSingle(),
            (r1,r2,r3) -> new int[]{r1, r2, r3})
            .subscribe(ints -> {
                System.out.println("result1:" + ints[0]);
                System.out.println("result2:" + ints[1]);
                System.out.println("result3:" + ints[2]);
            });
}

3. API简介

RxResult类非常简单,仅包含4个接口方法:

  1. postData方法
  • 方法签名:public void postData(T value)
  • 方法介绍:这个方法用于设置这个RxResult对象所持有的值,参数不可为空(如果想传空值,考虑使用Optional类),这个方法也不能多次调用(如果想要多次调用,持续地更新值,考虑使用RxLiveData)。
  1. getSingle方法
  • 方法签名:public Single<T> getSingle()
  • 方法说明:这个方法用于获得实现当前RxResult的Single对象,用于订阅结果,也可用于和RxJava其他功能整合。
  1. getValue方法
  • 方法签名:public T getValue()
  • 方法说明:用于获取当前值,如果还没有设置,就返回null
  1. optValue方法
  • 方法签名:public Optional<T> optValue()
  • 方法说明:这个方法是getValueOptional版本。

4. RxResult完整实现


import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleEmitter;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class RxResult<T> {
    private final Single<T> single;
    private List<SingleEmitter<T>> emitters = new LinkedList<>();

    private T value;

    public RxResult() {
        single = Single.create(emitter -> {
            emitters.add(emitter);
            if (Objects.nonNull(value)) {
                dispatchValue(value);
            }
        });
    }

    public Single<T> getSingle() {
        return single;
    }

    public void postData(T value) {
        if (Objects.isNull(value)) {//不接受空值作为结果,如果要表达空值,建议使用Optional
            throw new IllegalArgumentException("postData's argument must not be null!");
        } else if (Objects.nonNull(this.value)) {//不接受多次赋值,如果有多次赋值的需求,建议使用RxLiveData
            throw new IllegalStateException("postData has bean called, please do not call it again!");
        } else {
            this.value = value;
            dispatchValue(value);
        }
    }

    /* 将值分发给当前订阅者 */
    private void dispatchValue(T value) {
        List<SingleEmitter<T>> emitterList = this.emitters;
        this.emitters = new LinkedList<>();
        for (SingleEmitter<T> emitter : emitterList) {
            if (!emitter.isDisposed()) {
                emitter.onSuccess(value);
            }
        }
    }

    public Optional<T> optValue() {
        return Optional.ofNullable(value);
    }

    public T getValue() {
        return value;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值