Используйте RxJava для излучения Singleиз пожарного магазина дали несколько идентификаторов - PullRequest
1 голос
/ 21 марта 2019

Я создаю приложение, которое использует GeoFirestore для выполнения запросов на основе местоположения элементов, хранящихся в базе данных firestore.У меня есть функция, которая получает список всех идентификаторов, которые соответствуют критериям местоположения:

private fun loadIds(location: Location, distance: Double): Single<List<String>> {

    val query = geoFirestore.queryAtLocation(GeoPoint(location.latitude, location.longitude), distance)

    val ids = ArrayList<String>()

    return Single.create<List<String>> {
        query.addGeoQueryEventListener(object : GeoQueryEventListener {
            override fun onKeyEntered(uid: String, p1: GeoPoint?) {
                ids.add(uid)
            }

            override fun onGeoQueryReady() {
                it.onSuccess(ids)
            }

            override fun onKeyMoved(p0: String?, p1: GeoPoint?) {
            }
            override fun onKeyExited(p0: String?) {
            }
            override fun onGeoQueryError(p0: Exception?) {
            }
        })
    }
}

, но теперь у меня возникают проблемы при попытке объединить результаты элементов из каждого идентификатора в Single<List<Item>> каквозвращаемое значение.

Это то, что я делал раньше:

fun loadItems(ids: List<String>): Observable<Item> {

    return Observable.create<Item> { emitter ->
        for (id in ids) {
            val reference = firestore.collection("items").document(id)
            reference.get().addOnSuccessListener {
                emitter.onNext(it.toObject(Item::class.java)!!)
                if (ids.indexOf(id) == ids.size - 1) {
                    emitter.onComplete()
                }
            }
        }
    }
}

, где id был результатом loadIds().Это работало нормально, но в моей деятельности, где я это назвал, мне приходилось добавлять каждый элемент в упражнении по мере его выполнения, а затем прослушивать onComplete, который иногда срабатывал до того, как все элементы были загружены.

Я пытаюсь улучшить свой код и дополнительно отделить логику базы данных от своей деятельности, поэтому я хочу иметь возможность вернуть Single<List<Item>>, поэтому, как только я получу это в своей деятельности, я могу просто взять его изапустить.Я пытался понять это самостоятельно, но я довольно плохо знаком с RxJava и не совсем понимаю это.Вот моя последняя попытка:

fun loadItems(filter: Filter): Single<List<Item>> {

    return Single.create<List<Item>> { emitter ->

        val items= mutableListOf<Item>()

        loadIds(filter.currentLocation, filter.distance).map {
            for (uid in it) {
                getItem(uid).map {item ->
                    items.add(item)
                }.subscribe()
            }
        }.subscribe { _ ->
            emitter.onSuccess(items)
        }

    }

}

private fun getItem(uid: String): Single<Item> {
    return Single.create<Item> { emitter ->
        firestore.collection("items").document(uid).get().addOnSuccessListener {

            it.toObject(Item::class.java)?.let { item ->
                emitter.onSuccess(item)
            } ?: run {
                emitter.onError(Throwable("Error finding item"))
            }

        }.addOnFailureListener {
            emitter.onError(it)
        }
    }
}

, но, очевидно, onSuccess вызывается почти сразу, поэтому я не получаю никаких результатов.

1 Ответ

0 голосов
/ 22 марта 2019

Функция getItem выглядит хорошо, проблема заключается в функции loadItems.

Вы хорошо завернули механизм обратного вызова Firebase с функцией Single в getItem, но это не обязательно.в функции loadItems.Лучше сохранить одну цепочку в функции для удобства чтения (IMO).Это означает, что всякий раз, когда вы можете, не оборачивайте существующий реактивный объект (Observable, Single, Flowable) в subscribe, но используйте flatMap (или любую его версию).

fun loadItems(ids: List<String>): Single<List<Item>> {
    return Observable.just(ids)         // -> Observable<List<String>>
        .flatMapIterable { it }         // -> Observable<String>
        .flatMapSingle { getItem(it) }  // -> Observable<Item>
        // Create a single from the observable
        .collect<MutableList<Item>>(
            Callable { ArrayList<Item>() },
            BiConsumer { list, elem -> list.add(elem) }
        )   // -> Single<MutableList<Item>>
        .map { it } // -> Single<List<Item>>
}

Надеюсь, это поможет.

...