Как я могу вернуть один список из Rxjava вместо нескольких отправленных синглов?

#java #android #kotlin #rx-java #rx-android

#java #Android #kotlin #rx-java #rx-android

Вопрос:

У меня есть вызов Google place autocomplete sdk на Rxjava, который выдает мне список автозаполнения, а затем я использую этот список для итерации и вызова sdk place details от Google, с помощью которого я хочу вернуть единый список со всеми деталями places, но он не срабатывает.

 fun searchAutoComplete(word: String): Single<MutableList<SuggestedPlace>> {

    if (placeClient == null) {
        placeClient = this.context?.let { Places.createClient(it) }
    }
    return Observable.create<SuggestedPlace> { emiter ->
        var request = GooglePlaceHelper.getPlacesSuggestions(word)
        placeClient?.findAutocompletePredictions(request)
                ?.addOnSuccessListener { response: FindAutocompletePredictionsResponse ->
                    response.autocompletePredictions.forEach { place ->
                        var request = GooglePlaceHelper.getPlaceDetailRequest(place.placeId)
                        placeClient?.fetchPlace(request)
                                ?.addOnSuccessListener { response: FetchPlaceResponse ->
                                    val place = response.place
                                    place?.let {
                                        var suggestedPlace = SuggestedPlace(place.address!!, place.latLng?.latitude!!, place.latLng?.longitude!!)
                                        emiter.onNext(suggestedPlace)
                                    }
                                }
                    }
                }
    }.observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io()).toList()

}
 

Комментарии:

1. Код выглядит так, что в нем много мусора здесь и там. Трудно понять, в чем проблема.

2. полностью согласен @AnimeshSahu

Ответ №1:

Пожалуйста, предоставьте правильный пример в следующий раз. Издеваться над вашими API-интерфейсами довольно трудоемко.

импортируйте io.reactivex.rxjava3.core.Одиночный импорт org.junit.jupiter.api.Test

В этом примере GoogleAPI будет преобразован в a reactive -API, который предоставляет Single функции. Чтобы собрать все результаты в a List , вы могли бы использовать Single.zip .

Примечание: вы не должны использовать MutableList с RxJava. Всегда используйте неизменяемые типы данных, иначе у вас возникнут проблемы.

 class So65684080 {
    @Test
    fun so65684080() {
        val googleClientStub = GoogleClientStub()

        val reactiveClient = GoogleClientReactiveImpl(googleClientStub)

        val searchApiImpl = SearchApiImpl(reactiveClient)

        searchApiImpl.search("whatever")
            .test()
            .assertValue(listOf(SuggestedPlace("fetchPlace"), SuggestedPlace("fetchPlace")))
    }
}

internal interface SearchApi {
    fun search(word: String): Single<List<SuggestedPlace>>
}

internal class SearchApiImpl(private val client: GoogleClientReactive) : SearchApi {
    override fun search(word: String): Single<List<SuggestedPlace>> {
        return client.findAutocompletePredictions("whatever")
            .flatMap { resp ->
                val fetches = resp.values.map { r -> client.fetchPlace(r) }

                Single.zip(fetches) { arr ->
                    arr.map {
                        val fetchPlaceResponse = it as FetchPlaceResponse
                        SuggestedPlace(fetchPlaceResponse.name)
                    }
                        .toList()
                }
            }
    }
}

internal interface GoogleClient {
    fun findAutocompletePredictions(request: String): Result<FindAutocompletePredictionsResponse>

    fun fetchPlace(request: String): Result<FetchPlaceResponse>
}

internal interface GoogleClientReactive {
    fun findAutocompletePredictions(request: String): Single<FindAutocompletePredictionsResponse>

    fun fetchPlace(request: String): Single<FetchPlaceResponse>
}

internal class GoogleClientStub : GoogleClient {
    override fun findAutocompletePredictions(request: String): Result<FindAutocompletePredictionsResponse> {
        return ResultStub<FindAutocompletePredictionsResponse>(FindAutocompletePredictionsResponse(listOf("fetch1", "fetch2")))
    }

    override fun fetchPlace(request: String): Result<FetchPlaceResponse> {
        return ResultStub<FetchPlaceResponse>(FetchPlaceResponse("fetchPlace"))
    }
}

internal class GoogleClientReactiveImpl(private val client: GoogleClient) : GoogleClientReactive {
    override fun findAutocompletePredictions(request: String): Single<FindAutocompletePredictionsResponse> {
        return Single.create { emitter ->
            val response: (FindAutocompletePredictionsResponse) -> Unit = {
                emitter.onSuccess(it)
            }
            client.findAutocompletePredictions(request).addOnSuccessListener(response)
            // TODO: set emitter.setCancellable {} for unsubscribing
        }
    }

    override fun fetchPlace(request: String): Single<FetchPlaceResponse> {
        return Single.create { emitter ->
            val response: (FetchPlaceResponse) -> Unit = {
                emitter.onSuccess(it)
            }
            client.fetchPlace(request).addOnSuccessListener(response)
            // TODO: set emitter.setCancellable {} for unsubscribing
        }
    }
}

internal data class SuggestedPlace(val name: String)

internal data class FetchPlaceResponse(val name: String)

internal data class FindAutocompletePredictionsResponse(val values: List<String>)

internal interface Result<T> {
    fun addOnSuccessListener(response: (r: T) -> Unit)
}

internal class ResultStub<T>(val value: T) : Result<T> {
    override fun addOnSuccessListener(response: (r: T) -> Unit) {
        response(value)
    }
}
 

Примечание

Я не добавлял observeOn и subscribeOn , потому что это немного усложняет тестирование. Пожалуйста, добавьте его самостоятельно в конце Single формы SearchApiImpl#search