Библиотека подкачки Android не работает с асинхронными запросами

Библиотека подкачки 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.


person ashwin mahajan    schedule 02.05.2019    source источник


Ответы (1)


Я не понимаю, почему вы хотите перейти к основной теме. Методы загрузки в 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(..), ваш начальный список будет пустым, поэтому выполнение действий синхронно также позволяет избежать появления пустых списков.

person Josttie    schedule 19.06.2019
comment
Я понимаю, что вы говорите, но что, если список страниц, который он создает, зависит от нескольких асинхронных вызовов API... что-то вроде getApiID()... используйте идентификатор из этого в другом вызове. Я изо всех сил пытался понять, как создать список страниц из нескольких вызовов API. - person aidanmack; 07.11.2019