从事Android开发过程中,我们经常要在网络请求的时候显示“加载中”或“请求中”的对话框,然后在请求结束时显示对应状态或隐藏对话框,而手动关闭对话框时又要取消加载。相信类似需求应该经常碰到,有时候一个项目中会有好几个这样的地方,如果每个地方都要实现一遍类似的功能,那也是一件很郁闷的事。本文就展示一种通过增加一行代码来处理这种需求的工具–LoadingTransformer
。
1. 示例
首先来看一个示例:
Observable
.fromCallable(() -> {
Log.d(TAG, "test loading: ");
Thread.sleep(3000); //模拟长时间请求
return 1;
})
.subscribeOn(Schedulers.io())
.compose(new LoadingTransformer<>(context, "加载中..."))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(i -> {
//处理结果
}, throwable -> {
//处理异常
});
这里用sleep
来模拟长时间请求,通过添加一行compose(new LoadingTransformer<>(context, "加载中..."))
来实现加载过程中显示对话框,当加载完成的时候对话框消失。
在来看第二个示例:
Observable
.fromCallable(() -> {
Log.d(TAG, "test loading: ");
Thread.sleep(3000); //模拟长时间请求
if (new Random().nextBoolean()) {//随机出错,模拟异常情况
throw new RuntimeException("something wrong!");
}
return 1;
})
.subscribeOn(Schedulers.io())
.compose(new LoadingTransformer<>(view.getContext(), "加载中...", "加载失败", "加载结束"))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(i -> {
//处理结果
}, throwable -> {
//处理异常
});
这里我们给LoadingTransformer
提供了三个参数,分别表示加载中的文案、加载失败的文案以及加载结束的文案,这样对话框就会在对应的状态显示对应的内容。
2. LoadingTransformer的实现
import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import org.reactivestreams.Publisher;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.CompletableSource;
import io.reactivex.rxjava3.core.CompletableTransformer;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.FlowableTransformer;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.MaybeSource;
import io.reactivex.rxjava3.core.MaybeTransformer;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableSource;
import io.reactivex.rxjava3.core.ObservableTransformer;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleSource;
import io.reactivex.rxjava3.core.SingleTransformer;
import io.reactivex.rxjava3.disposables.Disposable;
public class LoadingTransformer<T> implements ObservableTransformer<T, T>,
SingleTransformer<T, T>,
MaybeTransformer<T, T>,
FlowableTransformer<T, T>,
CompletableTransformer { //实现了RxJava的各种Transformer,方便使用
private final AlertDialog dialog; //处于演示目的,使用AlertDialog,实际中用对应自定义弹窗替代
private String errorMsg;
private String completeMsg;
public LoadingTransformer(Context context, String s) {
dialog = new AlertDialog.Builder(context).setMessage(s).setCancelable(true).create();
}
public LoadingTransformer(Context context, String s, String errorMsg) {
dialog = new AlertDialog.Builder(context).setMessage(s).setCancelable(true).create();
this.errorMsg = errorMsg;
}
public LoadingTransformer(Context context, String s, String errorMsg, String completeMsg) {
dialog = new AlertDialog.Builder(context).setMessage(s).setCancelable(true).create();
this.errorMsg = errorMsg;
this.completeMsg = completeMsg;
}
private void onComplete() {
if (Objects.nonNull(completeMsg)) {
dialog.setMessage(completeMsg);
} else {
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
private void onError(Throwable t) {
if (Objects.nonNull(errorMsg)) {
dialog.setMessage(errorMsg);
} else {
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
@Override
public @NonNull CompletableSource apply(@NonNull Completable upstream) {
return upstream
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> dialog.setOnDismissListener(dialogInterface -> d.dispose()))
.doOnSubscribe((Disposable d) -> dialog.show())
.doOnComplete(this::onComplete)
.doOnError(this::onError);
}
@Override
public @NonNull MaybeSource<T> apply(@NonNull Maybe<T> upstream) {
return upstream
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> dialog.setOnDismissListener(dialogInterface -> d.dispose()))
.doOnSubscribe(d -> dialog.show())
.doOnComplete(this::onComplete)
.doOnError(this::onError);
}
@Override
public @NonNull ObservableSource<T> apply(@NonNull Observable<T> upstream) {
return upstream
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> dialog.setOnDismissListener(dialogInterface -> d.dispose()))
.doOnSubscribe(d -> dialog.show())
.doOnComplete(this::onComplete)
.doOnError(this::onError);
}
@Override
public @NonNull SingleSource<T> apply(@NonNull Single<T> upstream) {
return upstream
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> dialog.setOnDismissListener(dialogInterface -> d.dispose()))
.doOnSubscribe(d -> dialog.show())
.doOnSuccess(t -> onComplete())
.doOnError(this::onError);
}
@Override
public @NonNull Publisher<T> apply(@NonNull Flowable<T> upstream) {
return upstream
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(subscription -> dialog.setOnDismissListener(dialogInterface -> subscription.cancel()))
.doOnSubscribe(subscription -> dialog.show())
.doOnComplete(this::onComplete)
.doOnError(this::onError);
}
}
3. 注意
- 调用Dialog的
setOnDismissListener
方法时注意避免重复设置导致覆盖的情况,只设置一次就好。 - 如果要根据网络返回的结果(比如状态码)来提示异常,可以在
doOnSuccess
或doOnNext
中对结果进行判断并对应信息。