Объединение нескольких обратных вызовов Firebase с RxJava2 - PullRequest
0 голосов
/ 27 февраля 2020

во-первых, это чистый ад. Мне никогда не было так тяжело, как с адом обратного вызова. Я ненавижу это. Основная идея заключается в том, что у меня есть Firebase Storage и Firestore. В хранилище хранятся изображения, в то время как в облаке хранятся модели, имеющие ссылку на расположение изображений в хранилище. Затем я должен получить URL-адрес изображения из хранилища, чтобы я мог использовать его и загрузить с помощью glide. Пример: Облако Firebase дает мне drillType с (title = "blabla", imageUrl = "scg / asd / asd"), затем я передаю своему удаленному хранилищу imageUrl из "scg / asd / asd", и оно заменяет его URL-адресом, который Я могу использовать. Проблема в обратном вызове. Я не могу заставить это произойти так, как должно. Я пытался приостановить функции, наблюдатели и так далее. Идея состоит в том, что onNext должен вызвать getImageUrl и обновить DrillType с новым URL-адресом вместо места хранения. Но в настоящее время я не могу заставить onNext (или .map) ждать выполнения getImageUrl, прежде чем идти вперед.

Хранилище В настоящее время ЭТО НЕ РАБОТАЕТ

class DrillRepository(
    private val remoteDataSource: DataSource,
    private val remoteFilesDataSource: ImageUrlDataSource
) : CoroutineScope {


    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job


    fun loadDrillTypes(): Observable<DrillsType> {

        return Observable.create<DrillsType> { emitter ->
            val observer = object : DisposableObserver<DrillsType>() {
                override fun onError(e: Throwable) {
                    Log.d("TAG", "error " + e.message)
                    emitter.onError(e)
                }

                override fun onNext(data: DrillsType) {
                    data.let {
                    val singleObserver = object : SingleObserver<String>{
                        override fun onSuccess(t: String) {
                            it.drillType_imageUrl = t
                            dispose()
                        }

                        override fun onSubscribe(d: Disposable) {

                        }

                        override fun onError(e: Throwable) {
                            dispose()
                        }

                    }
                        getImageUrl(it.drillType_imageUrl).subscribeWith(singleObserver)
                    }
                }

                override fun onComplete() {
                    Log.d("TAG", "COMPLETE")
                    emitter.onComplete()
                    job.cancel()
                    dispose()
                }
            }

            remoteDataSource.loadDrillTypes().subscribeWith(observer)
        }
    }

fun getImageUrl(storageLocation: String): io.reactivex.Single<String> {
        return remoteFilesDataSource.getImageUrl(storageLocation)
    }
class FirebaseStorageDataSource(val firebaseStorage: FirebaseStorage) : ImageUrlDataSource {

    override fun getImageUrl(storageLocation: String): Single<String>
    {
        return Single.create<String>{emitter->
        firebaseStorage.getReferenceFromUrl(storageLocation).downloadUrl.addOnSuccessListener {
            emitter.onSuccess(it.toString())
        }.addOnFailureListener {
            emitter.onError(it)
        }
    }
class FirebaseFirestoreDataSource(val firebaseFirestore: FirebaseFirestore) : DataSource {


    override fun loadDrillTypes(): Observable<DrillsType> {

        return Observable.create<DrillsType> { emitter ->
            firebaseFirestore.collection("drilltypes")
                .get()
                .addOnSuccessListener { documents ->
                    for (document in documents) {
                        try {
                            val doc = document.toObject(DrillsType::class.java)
                            emitter.onNext(doc)
                        } catch (e: Exception) {
                            emitter.onError(e)
                        }
                    }
                    emitter.onComplete()
                }
                .addOnFailureListener { exception ->
                    Log.w("TAG", "Error getting documents: ", exception)
                    emitter.onError(exception)
                }
        }
    }
data class DrillsType(
    var drillType_title: String = "",
    var drillType_imageUrl: String = ""
)

Я получаю DrillTypes, но не могу обновить их ImageUrl. Мне как-то нужно дождаться выполнения, ie обратных вызовов от getImageUrl, а затем продолжить с onNext, используя обновленный DrillType.

Редактировать: я сталкивался с этой проблемой десятки раз. Это был самый ужасный еще. Я ищу, возможно, и желательно элегантный способ, как вы, ребята, решаете эту проблему. Также наблюдатель внутри наблюдателя абсолютно сумасшедший, но именно здесь я оказался после многих проб и ошибок.

РЕДАКТИРОВАТЬ2: РЕШЕНИЕ , надеюсь, полезно для будущих разработчиков

    fun loadFullDrillType(): Observable<DrillsType> {
        return loadDrillTypeWithRawImageUrl().flatMapSingle { drillTypeWithRawImageUrl ->
            loadImageUrl(drillTypeWithRawImageUrl.drillType_imageUrl).map {
                return@map drillTypeWithRawImageUrl.copy(drillType_imageUrl = it)
            }
        }
    }

    fun loadDrillTypeWithRawImageUrl(): Observable<DrillsType> {
        return remoteDataSource.loadDrillTypes()
    }
        return Observable.create<DrillsType> { emitter ->
            firebaseFirestore.collection("drilltypes")
                .get()
                .addOnSuccessListener { documents ->
                    try {
                        for (document in documents) {
                            val doc = document.toObject(DrillsType::class.java)
                            emitter.onNext(doc)
                        }
                    } catch (e: Exception) {
                        emitter.onError(e)
                    }
                    emitter.onComplete()
                }
                .addOnFailureListener { exception ->
                    Log.w("TAG", "Error getting documents: ", exception)
                    emitter.onError(exception)
                }
        }
    }

1 Ответ

0 голосов
/ 28 февраля 2020

Чтобы использовать RX в полной мере, каждая оболочка RX Firebase должна выполнять только одну операцию (например, выборку исходного типа детализации или просто выборку URL-адреса изображения). Затем вы используете RX-конструкции, такие как map и flatMap, чтобы объединить их:

fun loadDrillTypeWithRawImageUrl(): Single<DrillsType> = TODO()
fun loadDrillImageUrl(rawImageUrl: String): Single<String> = TODO()

fun loadFullDrillType(): Single<DrillsType> {
    return loadDrillTypeWithRawImageUrl().flatMap { rawDrillsType ->
        loadDrillImageUrl(rawDrillsType.drillType_imageUrl).map { newImageUrl ->
            DrillsType(rawDrillsType.drillType_title, newImageUrl)
        }
    }
}
...