2017-08-28 15 views
1

Web服務API有時使用分頁,其中Web服務調用的參數指示要檢索的頁面。這些可以被粗略地分爲兩種類型:我們如何使用RxJava進行分頁Web服務調用,其中每個頁面取決於上一頁響應,無需遞歸?

  • 問鼎其中參數來請求頁面獨立於任何給定的尋呼響應的(例如,「給我頁#3,具有10頁大小」)

  • 問鼎其中參數來請求頁面都依賴於以前的一些尋呼響應(例如,「給我項之後的下一個10個項目與foo標識符)

This SO answer覆蓋第一情景很好,在哪裏Web服務只需要一個頁面號,我們需要從任何給定頁面的響應中確定是否完成。

This SO answer涵蓋了第二種情況,但它依賴於遞歸,所以對於大數據集,我們將死亡StackOverflowError

A繼電器兼容GraphQL開發的Web服務(例如,GitHub的API)將大量使用第二個方案,因爲Relay's specification for pagination需要你提供一個「光標」從以前的響應來獲取光標之後的下一個項目位置。所以,我試圖找出一個非遞歸的方法,它仍然將所有東西都包裝成一個單一的主人Observable,這兩個答案的方式。

+0

你仍然可以使用「遞歸」的解決方案,具有一定的異步操作界面增強 - 例如'Flowable.observeOn(Schedulers.io())' - 內部'Flowable'調用 –

+0

Web服務API是否阻塞或返回Observable?問題是,爲了使用API​​,需要等待前一個結果完成以請求下一個結果。如果你願意阻止,那麼解決方案很容易。 – hgrey

回答

2

如果Web服務API被攔截,或者您願意阻止,然後將溶液用符號從遞歸的答案很簡單

Observable.generate(() -> new ApiResponse(page), (s, emitter) -> { 
    ApiResponse r = getResults(s.next);    
    emitter.onNext(r); 
    if (r.next == null) emitter.onComplete(); 
    return r; 
}); 

如果阻塞是不可取的,你可以使用FlowableTransformers.expandRxJava2Extensions像這樣

Flowable 
    .just(new ApiResponse(page)) 
    .compose(FlowableTransformers.expand(r -> r.next == null ? Flowable.empty() : getResults(r.next))); 
+0

這是一個有趣的觀點。我一直堅持使用現有的基於Observable的API,但我可以切換到使用阻塞調用並按照您的說明進行包裝。我會試一試 - 非常感謝! – CommonsWare

+0

請參閱第二部分更新的答案,它不會阻塞,但需要額外的庫 – hgrey

0

,如果我得到它的權利,我不知道,但我相信,你可以使用上RxJava SyncOnSubscribe.createStateful來解決這個問題。

看看我的樣品:

class SimpleTest { 
    @Test 
    fun testRequestTenPages() { 
    getPaginatedDataFromApi() 
     .take(10) 
     .subscribe { println(it) } 
    } 

    fun apiCall(previous: Response? = null) : Response { 
    return previous?.let { 
     val newPage = it.page + 1 
     previous.copy(id = "${it.id}_$newPage", page = newPage) 
    } ?: Response("1", 1) 
    }  

    fun getPaginatedDataFromApi(): Observable<Response> { 
    val syncOnSubscribe = SyncOnSubscribe.createStateful<Response?, Response>(
     { null }, 
     { previous, observer -> 
      val response = apiCall(previous) 
      observer.onNext(response) 
      [email protected] response 
     } 
    ) 

    return Observable.create(syncOnSubscribe) 
    } 

    data class Response(val id: String, val page: Int) 
} 

我創建一個有狀態的觀察到這使最後一個響應的狀態用它來生成一個響應。

運行這個測試,你會看到下面的輸出:

Response(id=1, page=1) 
Response(id=1_2, page=2) 
Response(id=1_2_3, page=3) 
Response(id=1_2_3_4, page=4) 
Response(id=1_2_3_4_5, page=5) 
Response(id=1_2_3_4_5_6, page=6) 
Response(id=1_2_3_4_5_6_7, page=7) 
Response(id=1_2_3_4_5_6_7_8, page=8) 
Response(id=1_2_3_4_5_6_7_8_9, page=9) 
Response(id=1_2_3_4_5_6_7_8_9_10, page=10) 
+0

這相當於Observable.generate中的其他答案和它類似,它也需要API調用阻塞 – hgrey

相關問題