MVP+RxJava ProgressDialog封装

第一版

封装一个包含DialogFragment 的Subscriber,


public class ProgressDialogSubscriber<T> implements FlowableSubscriber<T> {

  private static final String TAG = "ProgressSubscriber";
  private MyDialogFragment myDialogFragment;
  private FragmentManager fragmentManager;
  private Consumer<T> onNext;
  private Consumer<Throwable> onError;

  public ProgressDialogSubscriber(FragmentManager fragmentManager, Consumer<T> onNext,
      Consumer<Throwable> onError) {
    myDialogFragment = new MyDialogFragment();
    this.fragmentManager = fragmentManager;
    this.onNext = onNext;
    this.onError = onError;
  }

  @Override public void onSubscribe(Subscription s) {
    Log.i(TAG, "onSubscribe: ");
    myDialogFragment.show(fragmentManager, "dialog");
    s.request(1);
  }

  @Override public void onError(Throwable t) {
    Log.i(TAG, "onError: ");
    myDialogFragment.dismiss();
    try {
      onError.accept(t);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override public void onComplete() {
    Log.i(TAG, "onComplete: ");
    myDialogFragment.dismiss();
  }

  @Override public void onNext(T o) {
    Log.i(TAG, "onNext: " + o);
    myDialogFragment.dismiss();
    try {
      onNext.accept(o);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static class MyDialogFragment extends DialogFragment {

    @Nullable @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
      return inflater.inflate(R.layout.layout_dialog_fragment, container, false);
    }
  }
}

P层调用

    loginBiz.login(userName, pw)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new ProgressDialogSubscriber<UserEntity>(loginView.getSupportFragmentManager(),
            new Consumer<UserEntity>() {
              @Override public void accept(UserEntity userEntity) throws Exception {
                Log.i(TAG, "accept: userEntity=" + userEntity.getUserName());
              }
            }, new Consumer<Throwable>() {
          @Override public void accept(Throwable throwable) throws Exception {
            if (throwable instanceof LoginBiz.LoginInfoException) {
              Log.i(TAG, "accept:登录信息错误 " + throwable.getMessage());
            } else {
              Log.i(TAG, "accept: " + throwable.getMessage());
            }
          }
        }));

P层调用的时候,需要View层暴露getSupportFragmentManager(也有传入Context的)方法。而且Subscriber和P层依赖到了特定的Android平台代码,对纯业务逻辑有伤害,这样并不好。

第二版

隔离Android平台特有的context和getSupportFragmentManager。

ILoadingView 弹框控制接口

public interface ILoadingView {

  /**
   * 显示弹框
   */
  void show();

  /**
   * 关闭弹框
   */
  void dismiss();
}

LoadingSubscriber 定义的Subscriber只依赖控制ILoadingView

public class LoadingSubscriber<T> implements FlowableSubscriber<T> {

  private ILoadingView iLoadingView;

  private Consumer<T> onNext;
  private Consumer<Throwable> onError;

  public LoadingSubscriber(ILoadingView iLoadingView, Consumer onNext, Consumer onError) {
    this.iLoadingView = iLoadingView;
    if (iLoadingView == null) throw new NullPointerException("ILoadingView 不能为空");
    this.onNext = onNext;
    this.onError = onError;
  }

  @Override public void onSubscribe(Subscription s) {
    iLoadingView.show();
    s.request(1);
  }

  @Override public void onNext(T t) {
    iLoadingView.dismiss();
    if (onNext != null) {
      try {
        onNext.accept(t);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  @Override public void onError(Throwable t) {
    iLoadingView.dismiss();
    if (onError != null) {
      try {
        onError.accept(t);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  @Override public void onComplete() {
  }
}

View层 实现ILoadingView 接口,在P层调用如下

    loginBiz.login(userName, pw)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new LoadingSubscriber<UserEntity>(loginView, new Consumer<UserEntity>() {
          @Override public void accept(UserEntity userEntity) throws Exception {
            Log.i(TAG, "accept: userEntity=" + userEntity.getUserName());
          }
        }, new Consumer<Throwable>() {
          @Override public void accept(Throwable throwable) throws Exception {
            if (throwable instanceof LoginBiz.LoginInfoException) {
              Log.i(TAG, "accept:登录信息错误 " + throwable.getMessage());
            } else {
              Log.i(TAG, "accept: " + throwable.getMessage());
            }
          }
        }));

但是这个感觉还是有点问题,在订阅的时候使用了我自己特定的Subscriber,感觉还是不够干净清爽。能不能用链式的方式把弹框的控制逻辑加进去?

第三版

使用compose+doOnXXX

仍然使用上面的ILoadingView 接口。

  /**
   *这个必须放在subscribe之前,紧贴着subscribe,否则doOnComplete可能不会被调用
   * @param iLoadingView
   * @param <T>
   * @return
   */
  public    <T> FlowableTransformer waitLoadingTransformer(final ILoadingView iLoadingView) {

    return new FlowableTransformer<T, T>() {
      @Override public Publisher<T> apply(Flowable<T> upstream) {
        return upstream.doOnSubscribe(new Consumer<Subscription>() {
          @Override public void accept(Subscription subscription) throws Exception {
            Log.i(TAG, "doOnSubscribe accept: ");
            iLoadingView.show();
          }
        }).doOnNext(new Consumer() {
          @Override public void accept(Object o) throws Exception {
            Log.i(TAG, "doOnNext accept: ");
          }
        }).doOnError(new Consumer<Throwable>() {
          @Override public void accept(Throwable throwable) throws Exception {
            Log.i(TAG, "doOnError accept: ");
            iLoadingView.dismiss();
          }
        }).doOnComplete(new Action() {
          @Override public void run() throws Exception {
            Log.i(TAG, "doOnComplete run: ");
            iLoadingView.dismiss();
          }
        });
      }
    };
  }

Flowable.create(new FlowableOnSubscribe<Long>() {
      @Override public void subscribe(FlowableEmitter<Long> emitter) throws Exception {
        for (int i=1;i>0;i++) {
          Log.i(TAG, "subscribe: i=" + i +" Thread="+Thread.currentThread());
          emitter.onNext((long) i);
          try {
            Thread.sleep(2*1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
            return;
          }
          if (i ==10) {
            emitter.onError(new Throwable("TTT"));
            return;
          }
        }
      }
    },BackpressureStrategy.BUFFER)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .compose(waitLoadingTransformer(loginView)).subscribe(new FlowableSubscriber<Long>() {
      @Override public void onSubscribe(Subscription s) {
        Log.i(TAG, "onSubscribe: ");
        s.request(Integer.MAX_VALUE);
      }

      @Override public void onNext(Long aLong) {
        Log.i(TAG, "onNext: "+aLong);
      }

      @Override public void onError(Throwable t) {
        Log.i(TAG, "onError: "+t);
      }

      @Override public void onComplete() {
        Log.i(TAG, "onComplete: ");
      }
    });

这样是不是很纯粹很Rx的方式实现了对dialog的控制!

一直觉得可以用这样的方式去实现对dialog的控制,但是百度了很多,都是传入context之类的,觉得不纯粹,于是使用伟大的Google,发现了这个库RxLoading,参考了他的实现。

这是使用compose的方式实现的,AutoDispose 链式处理的思路 ObservableConverter+as操作符,感觉也是可以实现对dialog的控制,只是还没找到关于ObservableConverter 使用的介绍,看了半天,每太看懂这个怎么用,这个是2.1.7加入的实验性的功能。先暂时放置一下,后面再花时间好好的研究AutoDispose 以及ObservableConverter的使用。

第四版

在Google的时候,发现另外一种实现方案,使用using操作符

关于using操作符的介绍


    Flowable.using(new Callable<ILoadingView>() {
      @Override public ILoadingView call() throws Exception {
        Log.i(TAG, "using call: 11");
        //初始化
        loginView.show();
        return loginView;
      }
    }, new Function<ILoadingView, Publisher<?>>() {
      @Override public Publisher<?> apply(ILoadingView iLoadingView) throws Exception {
        Log.i(TAG, "using apply: 22");
        //发射数据的逻辑
        return  Flowable.create(new FlowableOnSubscribe<Long>() {
          @Override public void subscribe(FlowableEmitter<Long> emitter) throws Exception {
            for (int i=1;i>0;i++) {
              Log.i(TAG, "subscribe: i=" + i +" Thread="+Thread.currentThread());
              emitter.onNext((long) i);
              try {
                Thread.sleep(2*1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
                return;
              }
              if (i ==10) {
                emitter.onError(new Throwable("TTT"));
                return;
              }
            }
          }
        },BackpressureStrategy.BUFFER);
      }
    }, new Consumer<ILoadingView>() {
      @Override public void accept(ILoadingView iLoadingView) throws Exception {
        Log.i(TAG, "using accept:333 ");
        //释放资源
        iLoadingView.dismiss();
      }
    }).compose(rxLifeCycleHelper.io_main()).compose(rxLifeCycleHelper.bindTolifecycle(
        RxLifeCycleHelper.ActivityEvent.DESTROY)).subscribe(new FlowableSubscriber<Long>() {
      @Override public void onSubscribe(Subscription s) {
        Log.i(TAG, "onSubscribe: ");
        s.request(Integer.MAX_VALUE);
      }

      @Override public void onNext(Long aLong) {
        Log.i(TAG, "onNext: "+aLong);
      }

      @Override public void onError(Throwable t) {
        Log.i(TAG, "onError: "+t);
      }

      @Override public void onComplete() {
        Log.i(TAG, "onComplete: ");
      }
    });

可以直接在using的第一个参数中初始化Dialog,在第二个参数中,构造发射数据请求,第三个参数中释放dialog。

这种写法比较适合直接在Activity中初始化和显示Dialog,但是在MVP中显得不够友好,每次使用的时候写的有点复杂不好封装以通用,于是再次compose化。

using compose化

  public   <T> FlowableTransformer waitLoadingTransformerUsing(final ILoadingView iLoadingView) {

    return new FlowableTransformer<T, T>() {
      @Override public Publisher<T> apply(final Flowable<T> upstream) {
        return Flowable.using(new Callable<ILoadingView>() {
          @Override public ILoadingView call() throws Exception {
            Log.i(TAG, "using call: 11");
            //初始化
            iLoadingView.show();
            return iLoadingView;
          }
        }, new Function<ILoadingView, Publisher<? extends T>>() {
          @Override public Publisher<? extends T> apply(ILoadingView iLoadingView)
              throws Exception {
            Log.i(TAG, "using apply: 22");
            return upstream;
          }
        }, new Consumer<ILoadingView>() {
          @Override public void accept(ILoadingView iLoadingView) throws Exception {
            Log.i(TAG, "using accept:333 ");
            //释放资源
            iLoadingView.dismiss();
          }
        });
      }
    };
  }

使用,又链式了,很Rx。

Flowable.create(new FlowableOnSubscribe<Long>() {
      @Override public void subscribe(FlowableEmitter<Long> emitter) throws Exception {
        for (int i=1;i>0;i++) {
          Log.i(TAG, "subscribe: i=" + i +" Thread="+Thread.currentThread());
          emitter.onNext((long) i);
          try {
            Thread.sleep(2*1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
            return;
          }
          if (i ==10) {
            emitter.onError(new Throwable("TTT"));
            return;
          }
        }
      }
    },BackpressureStrategy.BUFFER).compose(waitLoadingTransformerUsing(loginView)).compose(rxLifeCycleHelper.io_main()).compose(rxLifeCycleHelper.bindTolifecycle(
        RxLifeCycleHelper.ActivityEvent.DESTROY)).subscribe(new FlowableSubscriber<Long>() {
      @Override public void onSubscribe(Subscription s) {
        Log.i(TAG, "onSubscribe: ");
        s.request(Integer.MAX_VALUE);
      }

      @Override public void onNext(Long aLong) {
        Log.i(TAG, "onNext: "+aLong);
      }

      @Override public void onError(Throwable t) {
        Log.i(TAG, "onError: "+t);
      }

      @Override public void onComplete() {
        Log.i(TAG, "onComplete: ");
      }
    });

方式四相对于方式三,不用写很多doOn方法,但是先onSubscribe 之后才执行using的第一个参数,方式三的doOnSubscribe 是在onSubscribe 之前执行。

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

推荐阅读更多精彩内容