Запускается ли PagingData в фоновом потоке по умолчанию в библиотеке подкачки 3?

Управляется ли фоновый поток автоматически с помощью PagingData, как и с PagedList, а затем возвращается в основной поток?

Из приведенных ниже журналов видно, что PagingData не запускается в фоновом потоке в библиотеке пейджинга 3 по сравнению с PagedList в библиотеке пейджинга 2.

Ожидать (На основе примера Paging Codelab)

  • GithubPagingSource override suspend fun load(...) для запуска в потоке ввода-вывода.
  • SearchRepositoriesActivity viewModel.searchRepo(query).collectLatest { ... } для запуска в основном потоке.

Наблюдать

  • И GithubPagingSource override suspend fun load(...), и SearchRepositoriesActivity viewModel.searchRepo(query).collectLatest { ... } выполняются в основном потоке.

Пейджинг 2

Потоки обрабатываются в фоновом режиме PagedList с toLiveData в соответствии с документацией.

Если вы используете LivePagedListBuilder для получения LiveData, он инициализирует PagedLists в фоновом потоке.

Страница 3

В документации по пейджингу 3 не упоминается, как осуществляется управление потоками. Однако, судя по журналам, PagingSource выполняет сетевой запрос в основном потоке и возвращает PagingData в основном потоке.

Мой пример кода

Я воссоздал шаблон Codelab в модуле CryptoTweets примера приложения app-simple. .

FeedPagingSource.kt

class FeedPagingSource : PagingSource<Int, Tweet>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Tweet> {
        try {
            val page = params.key ?: 1
            val nextPage = page + 1
            val tweets = Injection.feedService.getTweets(...)
            println("Thread: FeedPagingSource ${Thread.currentThread().name}")
            Log.v(LOG_TAG, "load success: ${tweets}")
            return LoadResult.Page(...)
        } catch (error: Exception) {
            ...
        }
    }
}

FeedRepository.kt

class FeedRepository {
    fun initFeed() = Pager(
        config = PagingConfig(pageSize = FEED_PAGEDLIST_SIZE),
        pagingSourceFactory = { FeedPagingSource() }
    ).flow
}

FeedViewModel.kt

repository.initFeed().onEach {
    println("Thread: FeedViewModel ${Thread.currentThread().name}")
    _feed.value = it
}.launchIn(viewModelScope)

Попытка решения

Чтобы запустить PagingSource в фоновом потоке, поток инициируется Dispatchers.IO. Однако журнал по-прежнему показывает PagingSource запусков в основном потоке в FeedPagingSource.kt.

FeedViewModel.kt

repository.initFeed().onEach {
            println("Thread: FeedViewModel ${Thread.currentThread().name}")
            _feed.value = it
}.flowOn(Dispatchers.IO).launchIn(viewModelScope)

person Adam Hurwitz    schedule 21.09.2020    source источник
comment
Попробуйте .launchIn(viewModelScope + Dispatchers.IO)   -  person IR42    schedule 21.09.2020
comment
Спасибо за рекомендацию @IR42. Однако это приводит к тому, что данные потока выдаются в потоке ввода-вывода, DefaultDispatcher-worker-1, а PagingSource все еще выполняется в main в приведенном выше коде.   -  person Adam Hurwitz    schedule 21.09.2020


Ответы (2)


Он не работает в фоновом потоке в пейджинге 3, даже я использую lifecycleScope.launch(Dispatchers.IO) в модели представления, поскольку доступ к PagingSource осуществляется из основного потока при загрузке адаптера. Итак, для Room я заработал, обернув код базы данных PagingSource внутри withContext(Dispatchers.IO) {

private const val STARTING_PAGE_INDEX = 0
private const val COUNT_PER_PAGE = 20

class LocalImagesPagingSource() : PagingSource<Int, GalleryImage>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GalleryImage> {
        val page = params.key ?: STARTING_PAGE_INDEX
        return try {
            withContext(Dispatchers.IO) {
                val dao = GalleryImageDatabase.getDatabase(context).galleryImageDao()
                val images = dao.getGalleryImagesByPosition(page * COUNT_PER_PAGE)

                LoadResult.Page(
                    data = images,
                    prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1,
                    nextKey = if (images.size == 0) null else page + 1
                )
            }
        } catch (exception: IOException) {
            return LoadResult.Error(exception)
        }
    }
}
person Alex Bin Zhao    schedule 22.05.2021
comment
У меня такая же проблема. Не могли бы вы опубликовать пример кода, который использует withContext(Dispatchers.IO) {? - person sinek; 26.05.2021
comment
Мое первоначальное решение — загружать локальные изображения из CursorLoader, а также сохранять и загружать дополнительную информацию из комнаты. Я упрощаю пример кода, добавляя только код, относящийся к комнате. - person Alex Bin Zhao; 27.05.2021

Котлин Корутины

При реализации PagingData и PagingSource с помощью Kotlin Coroutines и выполнении сетевого запроса Retrofit, как в приведенном выше примере кода, Retrofit по умолчанию обрабатывает фоновые потоки и возвращается в основной поток.

См. StackOverflow: Выполняет ли Retrofit сетевые вызовы в основном потоке?

RxJava

При использовании RxJava с Retrofit в качестве сетевого источника потоки должны быть явно указаны, как показано в образце документации Android.

См. документацию по Android: Страница из сети и базы данныхРеализовать RemoteMediator

person Adam Hurwitz    schedule 21.09.2020
comment
а как же номер?! - person A.sobhdel; 20.12.2020