RxJava和基于游标的RESTful分页

时间:2017-01-06 03:07:49

标签: java pagination retrofit rx-java retrofit2

我正在使用Spotify API,并希望使用RxJava链接一些分页结果。 Spotify使用基于游标的分页,因此像the one from @lopar这样的解决方案将无效。

回复来自this call,看起来像这样(想象有50 items):

{
  "artists" : {
    "items" : [ {
      "id" : "6liAMWkVf5LH7YR9yfFy1Y",
      "name" : "Portishead",
      "type" : "artist"
    }],
    "next" : "https://api.spotify.com/v1/me/following?type=artist&after=6liAMWkVf5LH7YR9yfFy1Y&limit=50",
    "total" : 119,
    "cursors" : {
      "after" : "6liAMWkVf5LH7YR9yfFy1Y"
    },
    "limit" : 50,
    "href" : "https://api.spotify.com/v1/me/following?type=artist&limit=50"
  }
}

现在,我正在使用改造:

获得这样的前50个结果
public class CursorPager<T> {
    public String href;
    public List<T> items;
    public int limit;
    public String next;
    public Cursor cursors;
    public int total;

    public CursorPager() {
    }
}

public class ArtistsCursorPager {
    public CursorPager<Artist> artists;

    public ArtistsCursorPager() {
    }
}

然后

public interface SpotifyService  {

    @GET("/me/following?type=artist")
    Observable<ArtistsCursorPager> getFollowedArtists(@Query("limit") int limit);

    @GET("/me/following?type=artist")
    Observable<ArtistsCursorPager> getFollowedArtists(@Query("limit") int limit, @Query("after") String spotifyId);

}

mSpotifyService.getFollowedArtists(50)
        .flatMap(result -> Observable.from(result.artists.items))
        .flatMap(this::responseToArtist)
        .sorted()
        .toList()
        .subscribe(new Subscriber<List<Artist>>() {
            @Override
            public void onNext(List<Artist> artists) {
                callback.onSuccess(artists);
            }
            // ...
        });

我想在callback.success(List<Artist>)中归还所有(在这种情况下为119)艺术家。我是RxJava的新手,所以我不确定是否有智能方式来执行此操作。

3 个答案:

答案 0 :(得分:5)

递归解决方案的唯一问题是堆栈溢出问题。没有递归的方法是

Observable<ArtistsCursorPager> allPages = Observable.defer(() ->
{
    BehaviorSubject<Object> pagecontrol = BehaviorSubject.create("start");
    Observable<ArtistsCursorPager> ret = pageControl.asObservable().concatMap(aKey ->
    {
        if (aKey != null && aKey.equals("start")) {
            return Observable.getFollowedArtists(50).doOnNext(page -> pagecontrol.onNext(page.cursors.after));
        } else if (aKey != null && !aKey.equals("")) {
            return Observable.getFollowedArtists(50,aKey).doOnNext(page -> pagecontrol.onNext(page.cursors.after));
        } else {
            return Observable.<ArtistsCursorPager>empty().doOnCompleted(()->pagecontrol.onCompleted());
        }        
    });
    return ret;
});

请参阅this question的解决方案。

答案 1 :(得分:1)

没有独特的方法来做到这一点。 就我而言,我所做的是使用mergeWith

进行一些递归调用
private Observable<String> getUUIDsQuery(JsonObject response) {
    final Observable<String> uuidsQuery = createUuidsQuery(response);
    return hasPagination(response) ? paginate(response, uuidsQuery) : uuidsQuery;
}

   private Observable<String> paginate(JsonObject response, Observable<String> uuidsQuery) {
    return request(getPaginationUri(response))
            .flatMap(res -> uuidsQuery.mergeWith(getUUIDsQuery(res)));
}

希望能帮助你一个想法。

答案 2 :(得分:0)

感谢您睁开眼睛,我没有正确地阅读您的问题。这是我可以建议你的最佳解决方案,而不使用正常的递归函数,但使用RxJava方式。

PublishSubject<Integer> limit = PublishSubject.create();

        limit.concatMap(integer -> Observable.just(integer + 1)) // Assuming this gives network result based upon the artist id you provided
                .doOnNext(integer -> {
                    // Based on the network result i make my conditions here. Pass the 50th artist id here. otherwise call onCompleted.
                    Timber.d("Publish: doOnNext: %d", integer);
                    if (integer < 10) {
                        // Pass the artist id to make the next call
                        limit.onNext(integer);
                    } else {
                        limit.onCompleted();
                    }
                })
                .toList()
                .subscribe(integers -> Timber.d("Publish: All the results"));

        limit.onNext(1); // For demo, I'm starting here with 1. You have to pass the artist id here

输出如下:

Publish: doOnNext: 2       
Publish: doOnNext: 3       
Publish: doOnNext: 4       
Publish: doOnNext: 5       
Publish: doOnNext: 6       
Publish: doOnNext: 7       
Publish: doOnNext: 8       
Publish: doOnNext: 9       
Publish: doOnNext: 10      
Publish: All the results

toList()运算符为您提供了在完成所有通话后最终得到的所有响应的列表。也请查看reduce()运算符。