简体   繁体   English

Android Paging 3 remoteMediator 使用 Compose 无限加载(APPEND)数据

[英]Android Paging 3 remoteMediator infinitely load(APPEND) data with Compose

I'm trying to implement a list with Paging 3 library using RemoteMediator.我正在尝试使用 RemoteMediator 实现一个带有 Paging 3 库的列表。
Initial loading is OK.初始加载正常。 Mediator loaded only 3 pages, which is set in 'PageConfig'.中介只加载了 3 个页面,这是在“PageConfig”中设置的。
But when scroll the list to trigger extra loading for more data, then Mediator starts loading data infinitely until it returns MediatorResult.Success(endOfPaginationReached = true) (Which means all data in remote was loaded).但是当滚动列表以触发额外加载更多数据时,Mediator 开始无限加载数据,直到它返回MediatorResult.Success(endOfPaginationReached = true) (这意味着远程中的所有数据都已加载)。 Even though scrolling is stopped.即使滚动停止。
I have no idea what makes Mediator keep loading.我不知道是什么让 Mediator 继续加载。
I want Mediator to load data only needed along scrolling.我希望 Mediator 加载仅在滚动时需要的数据。

Here is my code:这是我的代码:

@OptIn(ExperimentalPagingApi::class)
class PostRemoteMediator(
    private val postApi: ApiInterface,
    private val database: PostDatabase
) : RemoteMediator<Int, Post>() {

    override suspend fun load(loadType: LoadType, state: PagingState<Int, Post>): MediatorResult {
        return try {
            val userId = when (loadType) {
                LoadType.REFRESH -> {
                    logd(">> loadType.REFRESH")
                    STARTING_USER_ID
                }
                LoadType.PREPEND -> {
                    return MediatorResult.Success(endOfPaginationReached = true)
                }
                LoadType.APPEND -> {
                    logd(">> loadType.APPEND")

                    val lastItem = state.lastItemOrNull()
                        ?: return MediatorResult.Success(endOfPaginationReached = true)

                    lastItem.userId + 1
                }
            }

            logd(">> load data with userId = $userId")
            val response = postApi.getUserPosts(userId)

            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    database.postsDao().clearAll()
                }

                database.postsDao().insertAll(response?.body() ?: emptyList())
            }

            MediatorResult.Success(
                endOfPaginationReached = response.body().isNullOrEmpty()
            )
        } catch (e: IOException) {
            MediatorResult.Error(e)
        } catch (e: HttpException) {
            MediatorResult.Error(e)
        }
    }
}
@OptIn(ExperimentalPagingApi::class)
class PostRepositoryImpl @Inject constructor(
    private val remoteApi: ApiInterface,
    private val database: PostDatabase
) : PostRepository {
    override fun getUserPosts(): Flow<PagingData<Post>> {
        return Pager(
            config = PagingConfig(
                pageSize = 1
            ),
            remoteMediator = PostRemoteMediator(
                remoteApi,
                database
            )
        ) {
            // returns all data in table as PagingSource<Int, Post>
            database.postsDao().getPosts()
        }.flow
    }
}
@HiltViewModel
class PostViewModel @Inject constructor(
    private val postRepository: PostRepository
) : ViewModel() {
    private val TAG = PostViewModel::class.simpleName

    val postFlow: Flow<PagingData<Post>> = postRepository.getUserPosts().cachedIn(viewModelScope)
}

This is UI code:这是用户界面代码:

@Composable
fun PostList(postsFlow: Flow<PagingData<Post>>) {
    val posts = postsFlow.collectAsLazyPagingItems()

    LazyColumn(contentPadding = PaddingValues(horizontal = 8.dp)) {
        items(posts, key = { it.id }) { post ->
            post?.also { PostItem(userId = it.userId, id = it.id, content = it.body) }
        }

        posts.apply {
            when {
                loadState.mediator?.refresh is LoadState.Loading -> {
                    item { LoadingView(modifier = Modifier.fillParentMaxSize()) }
                }
                loadState.mediator?.append is LoadState.Loading -> {
                    item { LoadingView(modifier = Modifier.wrapContentHeight()) }
                }
                loadState.mediator?.refresh is LoadState.Error -> {
                    val error = loadState.refresh as LoadState.Error
                    item { ErrorView(error.error.localizedMessage!!, modifier = Modifier.fillParentMaxSize()) { retry() } }
                }
                loadState.mediator?.append is LoadState.Error -> {
                    val error = loadState.append as LoadState.Error
                    item { ErrorView(error.error.localizedMessage!!, modifier = Modifier.wrapContentHeight()) { retry() } }
                }
            }
        }
    }
}

Thanks for any answer感谢您的任何回答

Increase the pageSize when creating the PageConfig.创建 PageConfig 时增加 pageSize。

According to the documentation:根据文档:

Should be several times the number of visible items onscreen.应该是屏幕上可见项目数的几倍。 Configuring your page size depends on how your data is being loaded and used.配置页面大小取决于加载和使用数据的方式。 Smaller page sizes improve memory usage, latency, and avoid GC churn.较小的页面大小可以提高 memory 的使用率、延迟并避免 GC 流失。 Larger pages generally improve loading throughput, to a point (avoid loading more than 2MB from SQLite at once, since it incurs extra cost).较大的页面通常会在一定程度上提高加载吞吐量(避免一次从 SQLite 加载超过 2MB,因为它会产生额外的成本)。 If you're loading data for very large, social-media style cards that take up most of a screen, and your database isn't a bottleneck, 10-20 may make sense.如果您正在为占据大部分屏幕的非常大的社交媒体风格卡片加载数据,并且您的数据库不是瓶颈,那么 10-20 可能是有意义的。 If you're displaying dozens of items in a tiled grid, which can present items during a scroll much more quickly, consider closer to 100.如果您在平铺网格中显示数十个项目,这可以更快地在滚动期间显示项目,请考虑接近 100 个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM