我正在使用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);
答案 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