2017-01-06 41 views
4

我正在使用Spotify API,並希望使用RxJava鏈接幾個分頁結果。 Spotify使用基於光標的分頁,所以像the one from @lopar這樣的解決方案將無法工作。RxJava和基於光標的RESTful分頁

響應從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的新手,所以我不確定是否有智能這樣做。

+0

您是否知道在撥打此電話之前您要檢索的藝術家總數?如果你知道之前的大小,那麼使用'Observable.range(0,ARTIST_SIZE).buffer(YOUR_LIMIT)'你可以很容易地找回 –

+0

@AkbarShaEbrahim你不僅沒有讀過我的問題,它清楚地表明這個例子是119.不是你只是忽略了我指定的基於光標的分頁 - 這意味着你永遠不知道總數。最糟糕的是,你給了我一個答案,我在問題本身上連接。什麼也沒有。 –

+0

看看我的回答應該對你有幫助。 –

回答

3

遞歸解決方案的唯一問題是堆棧溢出問題。一種不用遞歸的方法是

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的解決方案。

+0

感謝您的幫助!這看起來很有前途,但並不像預期那樣工作。也許我在這個過程中犯了一個錯誤。我不清楚「start」是從哪裏來的,並且它會調用https://api.spotify.com/v1/me/follow?type = artist&limit = 50&after = start'。另外,我不得不將'page.cursors.after'更改爲'page.artists.cursors.after'。用'ArtistsCursorPager'的來源更新了問題。任何進一步的幫助,不勝感激。 –

+0

我在邏輯中犯了一個錯誤。檢查更正的代碼。你也是對的,'page.cursors.after'應該是'page.artists.cursors.after'我沒有仔細閱讀JSON。 – JohnWowUs

+0

輝煌。我提交了一個編輯以包含「aKey」的空檢查。謝謝! –

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))); 
} 

希望能幫助你,給你一個想法,做一些遞歸調用。

+0

是的,我現在有類似的東西,但想知道是否有更好的解決方案。謝謝你的時間! –

+0

據我所知,因爲我在一個月前提出了同樣的問題,沒有用於分頁的神奇算子 – paul

+0

你永遠不知道RxJava!請考慮提出問題,以防萬一有人有更好的解決方案。 –

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()運營商也是如此。

+0

感謝您的幫助,但您仍然誤解了這個問題。我正在處理一個數組,如果項目,沒有得到一個ID。我已經接受了@JohnWowUs給出的答案。 –