我正在一个Android应用程序上工作,它需要从网络中检索所有页面以显示屏幕上的详细信息。这是Youtube API https://developers.google.com/youtube/v3/docs/playlistItems/list
每个响应都有nextPageToken,它需要用于下一页api。应用程序不显示列表,所以分页是不需要的。我正在使用retrofit+flow+NetworkBoundResource+mvvm+kotlin。如何在存储库中使用任何转换方法FlatMapMerge/FlatMapConcat/FlatMapLatest来获得所有响应,然后发出。
restApiService
@GET(Contracts.PLAYLIST_ITEM_ENDPOINT)
fun getAllPlayListItemsForPlayListId(
@Query("part") part: String = "snippet,status",
@Query("playlistId") playlistId: String,
@Query("pageToken") pageToken: String = "",
@Query("maxResults") maxResults: Int? = 50
): Flow<ApiResponse<PlaylistItemsResponse>>
NetworkBoundResource
suspend fun asFlow(): Flow<Resource<ResultType>> {
return loadFromDb().transformLatest { dbValue ->
if (shouldFetch(dbValue)) {
emit(Resource.loading(dbValue))
createCall().collect { apiResponse ->
when (apiResponse) {
is ApiSuccessResponse -> {
withContext(Dispatchers.IO) {
saveCallResult(processResponse(apiResponse))
}
}
is ApiEmptyResponse -> {
emit(Resource.success(dbValue))
}
is ApiErrorResponse -> {
onFetchFailed()
emit(Resource.error(apiResponse.errorMessage, dbValue))
}
}
}
} else {
emit(Resource.success(dbValue))
}
}
}
库
override suspend fun createCall() =
apiService.getAllPlayListItemsForPlayListId(playlistId = playlistId,pageToken = nextPageToken)
如何递归调用相同的API,直到nextPagenToken为空,就像这样,这是在Go中实现的,参考Youtube API指南。https://developers.google.com/youtube/v3/docs/playlistItems/list
package main
import (
"fmt"
"log"
"google.golang.org/api/youtube/v3"
)
// Retrieve playlistItems in the specified playlist
func playlistItemsList(service *youtube.Service, part string, playlistId string, pageToken
string) *youtube.PlaylistItemListResponse {
call := service.PlaylistItems.List(part)
call = call.PlaylistId(playlistId)
if pageToken != "" {
call = call.PageToken(pageToken)
}
response, err := call.Do()
handleError(err, "")
return response
}
// Retrieve resource for the authenticated user's channel
func channelsListMine(service *youtube.Service, part string) *youtube.ChannelListResponse {
call := service.Channels.List(part)
call = call.Mine(true)
response, err := call.Do()
handleError(err, "")
return response
}
func main() {
client := getClient(youtube.YoutubeReadonlyScope)
service, err := youtube.New(client)
if err != nil {
log.Fatalf("Error creating YouTube client: %v", err)
}
response := channelsListMine(service, "contentDetails")
for _, channel := range response.Items {
playlistId := channel.ContentDetails.RelatedPlaylists.Uploads
// Print the playlist ID for the list of uploaded videos.
fmt.Printf("Videos in list %srn", playlistId)
nextPageToken := ""
for {
// Retrieve next set of items in the playlist.
playlistResponse := playlistItemsList(service, "snippet", playlistId, nextPageToken)
for _, playlistItem := range playlistResponse.Items {
title := playlistItem.Snippet.Title
videoId := playlistItem.Snippet.ResourceId.VideoId
fmt.Printf("%v, (%v)rn", title, videoId)
}
// Set the token to retrieve the next page of results
// or exit the loop if all results have been retrieved.
nextPageToken = playlistResponse.NextPageToken
if nextPageToken == "" {
break
}
fmt.Println()
}
}
}
我让它通过flatMapLatest工作。
fun getPageAndNext(playlistId: String, nextPageToken: String): Flow<ApiResponse<PlaylistItemsResponse>> {
return apiService.getAllPlayListItemsForPlayListId(playlistId = playlistId, pageToken = nextPageToken)
.flatMapLatest { apiResponse ->
var nextPage: String? = null
if (apiResponse is ApiSuccessResponse) {
nextPage = apiResponse.body.nextPageToken
}
if (nextPage == null) {
flowOf(apiResponse)
} else {
getPageAndNext(playlistId, nextPage)
}
}
}
override suspend fun createCall() = getPageAndNext(playlistId = playlistId, nextPageToken = nextPageToken)