d4vidi
d4vidi

Reputation: 2527

Inifinite loop loading items with paging3 and Jetpack compose UI

I have a simple app with a single screen, displaying movies in a Composable items list:

I use Android's paging3 library in order to load the movies page by page, and things seem to be working well:

@Composable
fun FlixListScreen(viewModel: MoviesViewModel) {
    val lazyMovieItems = viewModel.moviesPageFlow.collectAsLazyPagingItems()
    MoviesList(lazyMovieItems)
}

@Composable
fun MoviesList(lazyPagedMovies: LazyPagingItems<Movie>) {
    LazyColumn(modifier = Modifier.padding(horizontal = 16.dp)) {
        itemsIndexed(lazyPagedMovies) { index, movie ->
            MoviesListItem(index, movie!!)
        }
    }
}

In an attempt to add a progress indicator to the initial loading phase (e.g. as explained in an Android code-lab), I've tried applying the following conditional, based on loadState.refresh:

@Composable
fun FlixListScreen(viewModel: MoviesViewModel) {
    val lazyMovieItems = viewModel.moviesPageFlow.collectAsLazyPagingItems()

    // Added: Show a progress indicator while the data is loading
    if (lazyPagedMovies.loadState.refresh is LoadState.Loading) {
        LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
    }
    MoviesList(lazyMovieItems)
}

Instead of displaying the progress indicator, this naive addition seem to be putting the paging loader into an infinite loop, where the first page gets fetched over and over indefinitely, without any items effectively being loaded (let alone displayed) into the list.

Side note: Just to rule out that this all has something to do with the condition itself, it appears that even adding as little as this log: Log.i("DBG", ""+lazyPagesMovies.loadState) with no conditions at all, introduces the undesired behavior.

I'm using Kotlin version 1.7.10 and the various Compose libraries in version 1.3.1.

Upvotes: 2

Views: 1957

Answers (3)

Der Kicker
Der Kicker

Reputation: 21

The problem might be that you're reading a state (loadingState) and thus launching recomposition when it changes. Then your flow gets reinitialised and after being collected - launches refresh with the first page and so on.

You should remember the flow to prevent its recreation across recomposition.

Like that:

val lazyMovieItems = remember { viewModel.moviesPageFlow }.collectAsLazyPagingItems()

Upvotes: 2

Phyo Thiha Win
Phyo Thiha Win

Reputation: 99

As my problem with paging3 library, cause infinite loop in page-number 2 with continuous api calling. The main problem is cause of DAO's query ordering. DAO 's listing function has default sorting by primary-key.

In my case, api responses have their own sorting method. And I stored them into room db, get back them with simple select query in paging3 mediator. But DAO returns the listing with primary-key(id) ascending order automatically. So, the nextKey was always wrong and it happened infinite loop issue!!

@Entity(tableName = "home_banner")
data class HomeBannerEntity(
    @PrimaryKey(autoGenerate = true) val tableId: Long = 0L,
    val id: Int,
    val name: String,
    val image: String
)

So, like above code, add table autoGenerated id and make dao sort by insert ordering.

Upvotes: 0

d4vidi
d4vidi

Reputation: 2527

Seems that with this simple code I might have somehow hit some Compose related edge-case. I've managed to work around things by introducing the progress-indicator conditional under a sub-function (composable) that accepts the paging items directly:

@Composable
fun FlixListScreen(viewModel: MoviesViewModel) {
    val lazyMovieItems = viewModel.moviesPageFlow.collectAsLazyPagingItems()
    MoviesScreen(lazyMovieItems) // was: MoviesList(lazyMovieItems)
}

// Newly added intermediate function
@Composable
fun MoviesScreen(lazyPagedMovies: LazyPagingItems<Movie>) {
    MoviesList(lazyPagedMovies)

    if (lazyPagedMovies.loadState.refresh is LoadState.Loading) {
        LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
    }
}

@Composable
fun MoviesList(lazyPagedMovies: LazyPagingItems<Movie>) {
    // ... (unchanged)
}

Upvotes: 2

Related Questions