Опубликовать несколько результатов из LiveDataReactiveStreams.fromPublisher () внутри вложенной цепочки RxJava - PullRequest
0 голосов
/ 30 сентября 2019

Давайте начнем с базового примера,

Предположим, у меня есть следующий блок кода для получения Auth Token из репозитория

private fun getToken(): LiveData<TokenResponse> {
        return LiveDataReactiveStreams.fromPublisher(remoteSource.getAuthToken()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map { token -> token }
        )
    }

Я получаю LiveData результат в ViewModel

class HomeModel {

        private var tokenLiveDataSource: MediatorLiveData<TokenResponse> = MediatorLiveData()
        private var tokenLiveData: MutableLiveData<TokenResponse> = MutableLiveData()

        fun observeTokenLiveData(): LiveData<TokenResponse> {
            return tokenLiveData
        }

        fun getToken() {
            val source = repository.getToken()
            tokenLiveDataSource.addSource(source) {
                tokenLiveData.value = it
                tokenLiveDataSource.removeSource(source)
            }
        }
    }

На данный момент все работает отлично, как и ожидалось. Однако проблема возникает, когда я хочу объединить 2 запроса вместе и получить отдельные LiveData для каждого из них. Допустим, я хочу вызвать API поиска после получения результата от Token API . Моя цель - получить 2 LiveData изменений для каждого шага. Ниже приведен модифицированный код для демонстрации моих требований:

private fun getSearchResult(): LiveData<?> {
    return LiveDataReactiveStreams.fromPublisher(remoteSource.getAuthToken() // 1ST API CALL
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .map { token ->
                // NOTIFY THE 1ST API RESULT/RETURN LiveData<TokenResponse>
                token
            }
            .flatMap { token ->
                remoteSource.getSearchResult(token) // 2ND API CALL
            }
            .map { results ->
                // NOTIFY THE 2ND API RESULT/RETURN LiveData<SearchResult>
                results
            }

    )
}

Как получить 2 LiveData из данного примера?

class HomeModel {

            private var tokenLiveDataSource: MediatorLiveData<TokenResponse> = MediatorLiveData()
            private var tokenLiveData: MutableLiveData<TokenResponse> = MutableLiveData()

            private var searchLiveDataSource: MediatorLiveData<SearchResponse> = MediatorLiveData()
            private var searchLiveData: MutableLiveData<SearchResponse> = MutableLiveData()

            fun observeTokenLiveData(): LiveData<TokenResponse> {
                return tokenLiveData
            }

            fun observeSearchLiveData(): LiveData<SearchResponse> {
                return searchLiveData
            }

            fun getSearchResult() {
                // RECEIVE 2 LiveData HERE
            }
        }

1 Ответ

1 голос
/ 30 сентября 2019

Создайте класс-оболочку, который может иметь как токен, так и результат поиска

data class TokenAndSearchResult(
    val tokenResponse: TokenResponse,
    val searchResponse: SearchResponse
)

Тогда хранилище:

private fun getSearchResult(): LiveData<TokenAndSearchResult> {
    return LiveDataReactiveStreams.fromPublisher(remoteSource.getAuthToken()
            .subscribeOn(Schedulers.io())
            // .observeOn(AndroidSchedulers.mainThread()) probably won't be needed. LiveData is observed in the main thread anyway.
            .flatMap { token ->
                remoteSource.getSearchResult(token) // Flowable<SearchResult>
                    .map { result -> TokenAndSearchResult(token, result) } // Flowable<TokenAndSearchResult>
                    .startWith(TokenAndSearchResult(token, SearchResult()))) // Flowable<TokenAndSearchResult>
            } // Flowable<TokenAndSearchResult>

    )
}

В результате LiveData<TokenAndSearchResult> выдаст один экземпляр TokenAndSearchResult с токеном. Этот начальный экземпляр будет иметь пустой результат поиска, представленный SearchResult() в приведенном выше примере кода.

Как только remoteSource.getSearchResult() вернется, он сгенерирует второй раз с непустым значением SearchResult.

ViewModel может сопоставить этот результат LiveData с отдельным LiveData

class HomeModel {

    // Declaring "var LiveData" is usually an anti-pattern, because
    // LiveData shouldn't change but only the object wrapped by LiveData
    // should change.
    private val tokenLiveData = MediatorLiveData<TokenResponse>() 
    private val searchLiveData = MediatorLiveData<SearchResponse>()

    fun observeTokenLiveData(): LiveData<TokenResponse> {
        return tokenLiveData
    }

    fun observeSearchLiveData(): LiveData<SearchResponse> {
        return searchLiveData
    }

    fun getSearchResult() {
        val source = repository.getSearchResult()
        tokenLiveData.addSource(source) {
            tokenLiveData.value = it.tokenResponse
            tokenLiveData.removeSource(source)
        }
        searchLiveData.addSource(source) {
            searchLiveData.value = it.searchResponse
            searchLiveData.removeSource(source)
        }
    }
}

Дополнительная мысль:

Как только приложение завершает процесс аутентификации и получает токен аутентификации, приложение обычнохочет сохранить этот токен аутентификации и не повторять этот процесс аутентификации, пока токен не истечет. Также он должен иметь логику, которая обнаруживает ошибку аутентификации 401 или 403 и повторяет попытки после обновления токена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...