Библиотека Android Paging не работает с асинхронными запросами - PullRequest
0 голосов
/ 02 мая 2019

Библиотека подкачки Android не работает при выполнении асинхронных сетевых вызовов с использованием Retrofit.Я использую пример кода Google для компонентов архитектуры на Github и изменил его для своих нужд.

Ранее я сталкивался с той же проблемой, но обошел ее, сделав синхронный вызов, так как сценарий использования позволил это.Но в текущем сценарии требуется несколько сетевых вызовов, и хранилище данных возвращает объединенный результат.Я использую RxJava для этой цели.

Изначально это выглядело как проблема с многопоточностью, но этот ответ предполагает иное.Наблюдение за вызовом RxJava в главном потоке также не работает.

Я добавил соответствующий код ниже.Я вошел в callback.onResult во время отладки, и все работает, как ожидалось.Но в конечном итоге он не уведомляет Recycler View Adapter.

View Model snippet:

open fun search(query : String, init : Boolean = false) : Boolean {
    return if(query == searchQuery.value && !init) {
        false
    } else {
        searchQuery.value = query
        true
    }
}

fun refresh() {
    listing.value?.refresh?.invoke()
}

var listing : LiveData<ListingState<T>> = Transformations.map(searchQuery) {
    getList() // Returns the Listing State from the Repo snippet added below.
}

Repository snippet:

val dataSourceFactory = EvaluationCandidateDataSourceFactory(queryParams,
            Executors.newFixedThreadPool(5) )

    val pagelistConfig = PagedList.Config.Builder()
            .setEnablePlaceholders(true)
            .setInitialLoadSizeHint(5)
            .setPageSize(25)
            .setPrefetchDistance(25).build()

    val pagedList = LivePagedListBuilder<Int, PC>(
            dataSourceFactory, pagelistConfig)
            .setFetchExecutor(Executors.newFixedThreadPool(5)).build()

    val refreshState = Transformations.switchMap(dataSourceFactory.dataSource) {
        it.initialState
    }

    return ListingState(
            pagedList = pagedList,
            pagingState = Transformations.switchMap(dataSourceFactory.dataSource) {
                it.pagingState
            },
            refreshState = refreshState,
            refresh = {
                dataSourceFactory.dataSource.value?.invalidate()
            },

            retry = {
                dataSourceFactory.dataSource.value?.retryAllFailed()
            }
    )

Data Source snippet :

override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, PC>) {
    try {
        queryMap = if (queryMap == null) {
            hashMapOf("page" to FIRST_PAGE)
        } else {
            queryMap.apply { this!!["page"] = FIRST_PAGE }
        }

        initialState.postValue(DataSourceState.LOADING)
        pagingState.postValue(DataSourceState.LOADING)
        val disposable : Disposable = aCRepositoryI.getAssignedAC(queryMap)
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    if(it.success) {
                        // remove possible retries on success
                        retry = null

                        val nextPage = it.responseHeader?.let { getNextPage(it, FIRST_PAGE) } ?: run { null }
                        val previousPage = getPreviousPage(FIRST_PAGE)
                        callback.onResult(it.response.pcList, previousPage, nextPage)
                        initialState.postValue(DataSourceState.SUCCESS)
                        pagingState.postValue(DataSourceState.SUCCESS)
                    } else {
                        // let the subscriber decide whether to retry or not
                        retry = {
                            loadInitial(params, callback)
                        }
                        initialState.postValue(DataSourceState.failure(it.networkError.message))
                        pagingState.postValue(DataSourceState.failure(it.networkError.message))
                        Timber.e(it.networkError.message)
                    }
                }, {
                    retry = {
                        loadInitial(params, callback)
                    }
                    initialState.postValue(DataSourceState.failure(it.localizedMessage))
                    pagingState.postValue(DataSourceState.failure(it.localizedMessage))
                })
    } catch (ex : Exception) {
        retry = {
            loadInitial(params, callback)
        }
        initialState.postValue(DataSourceState.failure(ex.localizedMessage))
        pagingState.postValue(DataSourceState.failure(ex.localizedMessage))
        Timber.e(ex)
    }
}

Может кто-нибудь сказать, в чем здесь проблема?Существует аналогичная проблема , о которой я упоминал выше, но она рекомендует использовать синхронные вызовы.Как мы можем сделать это с помощью асинхронных вызовов или с помощью RxJava.

1 Ответ

0 голосов
/ 19 июня 2019

Я не понимаю, почему вы хотите перейти к основной теме. Методы загрузки в DataSource работают в фоновом потоке. Это означает, что вы можете выполнять синхронную работу в этом потоке, не блокируя основной поток, а это означает, что вы можете просто придумать решение без RxJava. Что-то вроде:

override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, PC>) {
    try {
        val result = repository.fetchData(..)
        // post result values and call the callback
    catch (e: Exception) {
        // post error values and log and save the retry
    }
}

Тогда в вашем хранилище вы можете сделать это, потому что мы не в главном потоке.

fun fetchData(...) {
    val response = myRetrofitService.someBackendCall(..).execute()
    response.result?.let {
        return mapResponse(it)
    } ?: throw HttpException(response.error)
}

Возможно, я ошибся в синтаксисе, но надеюсь, вы поняли. Нет обратных вызовов, нет подписки / наблюдения, но простой и понятный код.

Также, если вы начнете создавать потоки внутри метода loadInitial (..), ваш начальный список будет пустым, поэтому синхронная работа также позволяет избежать просмотра пустых списков.

...