RxJava和Retrofit处理多个平面图的分页

时间:2017-04-28 15:36:25

标签: android pagination retrofit rx-java observable

我正在使用Retrofit RxJava,我需要关注登录场景:

1)使用密码登录用户并获取访问令牌

2)获取用户个人资料, 朋友(需要分页假设15页), 对话(需要分页假设10页)

第二步的所有请求都需要令牌,因此它们依赖于第一步。

我需要做的是执行第一步,当它完成后执行第二步,并在完成所有3个工作时收到通知。

我现在拥有的:

首先,我使用concatWith运算符来处理分页,并且它的工作效果很好。例如:

mApi.getConversationsByPage(accessToken,page,take)
.concatMap(convResponse -> {
  if(convResponse.body().getNextPageUrl().equals("NA")) {
      return Observable.just(convResponse);
  } else {
      return Observable.just(convResponse)
      .concatWith(getConversationsAndNext(accessToken,convResponse .body().getNextPage(),take));
  }
});

我的代码:

mService.loginUser(email,password)
     .subscribeOn(mSchedulerProvider.io())
     .observeOn(mSchedulerProvider.ui())
     .flatMap(tokenResponse -> {
            //login finished save token
            mService.saveUserToken(tokenResponse.body());
            //first step done go to second step
            return mService.getUserProfile(mService.getToken();
       })
     .flatMap(profileResponse -> {
         mService.saveUserProfile(profileResponse.body());
         return mService.getUserFriendsAndNext(mService.getToken(),FIRST_PAGE,TAKE_PERPAGE);
     }).doOnNext(friendResponse -> mService.handleFriendPaging(friendResponse.body().getData()))
     .flatMap(friendResponse -> {
         mService.saveAllFriends();
         return mService.getConversationsAndNext(mService.getToken(),FIRST_PAGE,TAKE_PERPAGE);
     })
     .subscribe(new Observer<Response<ConvResult>>() {
         @Override
         public void onSubscribe(Disposable d) {
             mDisposables.add(d);
         }

         @Override
         public void onNext(Response<ConvResult> convResponse) {
             mService.handleConvPaging(convResponse .body().getData());   
         }

         @Override
         public void onError(Throwable e) {
             //handle error
         }

         @Override
         public void onComplete() {
             //all requests done handle it
             mService.saveAllFriends();
         }
     });

一切都很好。我正在保存用户令牌,用户个人资料和用户朋友,但是当它进入对话mService.handleConvPaging方法时,会调用25次而不是10次。

我的测试方法:Mockito.verify(mService,times(10)).handleConvPaging();

错误:

org.mockito.exceptions.verification.TooManyActualInvocations: 
mService.handleConvPaging();
Wanted 10 times:
-> at ........
But was 25 times. 

所以我想做的是:独立地获取用户朋友和对话,并在所有请求,请求通知完成后通知。

有没有办法在不使用嵌套回调的情况下实现这一目标?提前谢谢。

@Edit

mService.loginUser(email,password)
            .flatMap(tokenResponse -> {
                if(tokenResponse.isSuccessful()) {
                    mService.saveUserToken(tokenResponse.body());
                    return mService.getUserProfile(mService.getUserAccessToken());
                } else {
                    return Observable.error(new Exception("login token error"));
                }
            })
            .flatMap(profileResponse -> {
                if(profileResponse.isSuccessful()) {
                    mService.saveUserProfile(profileResponse.body());
                    return mService.getUserFriendsAndNext(mService.getUserAccessToken(),1,Constants.TAKE_COUNT);
                }else {
                    return Observable.error(new Exception("login profile error"));
                }
            })
            .doOnNext(friendResponse -> {
                if(friendResponse.isSuccessful()) mService.handleFriendPaging(friendResponse.body().getData());
            })
            .ignoreElements()
            .andThen(mService.getConversationsAndNext(mService.getUserAccessToken(),1,Constants.TAKE_COUNT))
            .subscribe(onsubscribe,onnext,onerror,oncomplete) 

如果我在Observable.error()部分返回tokenResponse andThen方法正在调用,我不希望发生这种情况

我还会在mService.loginUser方法调用时返回此信息:

Response<UserTokenResult> tokenResultResponse = Response.error(400,mResponseBody);

1 个答案:

答案 0 :(得分:2)

正如您在评论中提到的,您的问题是在第二个flatMap完成后应该调用第三个flatMap。 所以我想提出这样的解决方案:

    mService.loginUser(email,password)
            .subscribeOn(mSchedulerProvider.io())
            .observeOn(mSchedulerProvider.ui())
            .flatMap(tokenResponse -> {
                mService.saveUserToken(tokenResponse.body());
                return mService.getUserProfile(mService.getToken();
            })
            .flatMap(profileResponse -> {
                mService.saveUserProfile(profileResponse.body());
                return mService.getUserFriendsAndNext(mService.getToken(),FIRST_PAGE,TAKE_PERPAGE);
            })

            // do All your work with your Friends in this operator
            .doOnNext(friendResponse -> mService.handleFriendPaging(friendResponse.body().getData()))

            //his will return you a Completable
            .ignoreElements()

            //You get the signal means the upStream works are done
            .andThen(mService.getConversationsAndNext(mService.getToken(),FIRST_PAGE,TAKE_PERPAGE))
            .subscribe(new Observer<Response<ConvResult>>() {
                @Override
                public void onSubscribe(Disposable d) {
                    mDisposables.add(d);
                }

                @Override
                public void onNext(Response<ConvResult> convResponse) {
                    mService.handleConvPaging(convResponse .body().getData());
                }

                @Override
                public void onError(Throwable e) {
                    //handle error
                }

                @Override
                public void onComplete() {
                    //This is somehow not necessary
                    mService.saveAllFriends();
                }
            });

所以你保留一个单一的流并完成你的工作。

ignoreElements()提供只接收终端事件的能力(onComplete()和onError()),andThen()将订阅upStream Completable,并将继续从您放入的源发出项目andThen()

我测试了像

这样的东西

我试过像

这样的东西
    Observable.just("A", "B", "C")
            .flatMap(x -> Observable.error(new Throwable("Eorror")))
            .doOnNext(x -> System.out.println(x))
            .ignoreElements()
            .andThen(Observable.just("New D"))
            .subscribe(x -> System.out.println("onNext" + x),
            error -> System.out.println("onError" + error),
            () -> System.out.println("END"));

并且只打印

System.out: onErrorjava.lang.Throwable: Eorror

使用RxJava版本2.0.9