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