android / codelab-android-paging

Jetpack Paging codelab
Apache License 2.0
495 stars 268 forks source link

Does PagingData Run on a Background Thread by Default in Paging 3 Library? #109

Closed adam-hurwitz closed 4 years ago

adam-hurwitz commented 4 years ago

Is background threading managed automatically with PagingData as it is with PagedList, and then returned on the main thread?

From the logs below, it appears PagingData is not run on the backgroud thread in the Paging 3 library compared to PagedList in Paging 2. I've created logs within the Codelab sample inside the GithubPagingSource and SearchRepositoriesActivity classes, and re-created in my sample code below.

Expect

Observe

Paging 2

Threading is handled on the background by PagedList with toLiveData according to the documentation.

If you use LivePagedListBuilder to get a LiveData, it will initialize PagedLists on a background thread for you.

Paging 3

The Paging 3 documentation does not mention how threading is managed. However, from the logs, PagingSource appears to be running the network request on the main thread and returning the PagingData on the main thread.

Sample

I've recreated the Codelab pattern in the CryptoTweets sample app app-simple module.

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)

Attempted Solution

In order to run the PagingSource on a background thread, the flow is initiated on Dispatchers.IO. However, the log still shows PagingSource runs on the main thread in FeedPagingSource.kt.

FeedViewModel.kt

repository.initFeed().onEach {
            println("Thread: FeedViewModel ${Thread.currentThread().name}")
            _feed.value = it
}.flowOn(Dispatchers.IO).launchIn(viewModelScope)
adam-hurwitz commented 4 years ago

Kotlin Coroutines

When implementing PagingData and PagingSource with Kotlin Coroutines and making a Retrofit network request, as in the sample code above, Retrofit handles the background threading by default and returns on the main thread.

See StackOverflow: Does Retrofit make network calls on main thread?

RxJava

When using RxJava with Retrofit as the network source, the threading needs to be explicitly specified as shown in the Android documentation sample.

See Android documentation: Page from network and database > Implement a RemoteMediator

droidevs commented 7 months ago

Kotlin Coroutines

When implementing PagingData and PagingSource with Kotlin Coroutines and making a Retrofit network request, as in the sample code above, Retrofit handles the background threading by default and returns on the main thread.

See StackOverflow: Does Retrofit make network calls on main thread?

RxJava

When using RxJava with Retrofit as the network source, the threading needs to be explicitly specified as shown in the Android documentation sample.

See Android documentation: Page from network and database > Implement a RemoteMediator

i use local database in paging source [ROOM] can i have a solution to run the room calls in the background