在 Jetpack Compose 中,如何具体处理 paging3 中的错误并显示在 ui 上?

In Jetpack Compose, how do I specifically handle errors in paging3 and display them on ui?

在我的例子中,我请求 GitHub API,如果我请求大量数据,我可能会收到 403 错误,而如果我 运行请求的数据,GitHub 将 return 我一个 422 不可处理的实体。所以我想根据这个在UI提示用户是被API限速还是没有新数据

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, EventWithRepoInfo> {
        val position = params.key ?: GITHUB_STARTING_PAGE_INDEX
        return try {
            val receivedEvents = network.receivedEvents(token, network.getUserInfo(token).login, position, params.loadSize)
            val eventWithRepoInfo: MutableList<EventWithRepoInfo> = mutableListOf()
            val nextKey = if (receivedEvents.isEmpty()) {
                null
            } else {
                position + (params.loadSize / NETWORK_PAGE_SIZE)
            }
            nextKey?.let {
                receivedEvents.forEach {
                    eventWithRepoInfo.add(EventWithRepoInfo(it, ktorClient.getRepoInfo(token, it.repo.url)))
                }
            }
            LoadResult.Page(
                data = eventWithRepoInfo,
                prevKey = if (position == GITHUB_STARTING_PAGE_INDEX) null else position - 1,
                nextKey = nextKey
            )
        } catch (exception: IOException) {
            exception.printStackTrace()
            return LoadResult.Error(exception)
        } catch (exception: HttpException) {
            exception.printStackTrace()
            return LoadResult.Error(exception)
        }
    }

所以我想判断这个地方是403还是422。如果是422会显示没有更多数据,403会提示用户api限速

    LazyColumn {
        events.apply {
            when {
                loadState.append is LoadState.Loading -> {
                    item {
                            Text("loading")
                        }
                    }
                }
                // I want to show different things here depending on different error codes
                loadState.append is LoadState.Error -> {
                    item {
                        LargeButton(onClick = { events.retry() }) {
                            Text("loading error, retry.")
                        }
                    }
                }
            }
        }
    }

这里有很多东西,如果你真的想使用分页的力量(所以,只在用户滚动时加载项目),你需要使用 LazyColumn 提供的 items 方法,即接受参数 a LazyPagingItems<T>

首先,您应该从寻呼机中检索 PagingData 流。

val githubPager = Pager(...)
val pagingItems : Flow<PagingData<MyCustomDto>> = githubPager.flow

一旦获得分页数据流,理想情况下,您应该将其缓存在视图模型范围内,然后将其公开给视图

val cachedPagingItems = pagingItems.cachedIn(viewModelScope)

在你的内部是可组合的,你现在可以收集 pagingItems,将错误传输到你的视图模型,并在你的 LazyColumn 中显示分页数据

我做了一个小的扩展函数来执行remember操作,直接暴露一个重试块:

@Composable
fun <T : Any> Flow<PagingData<T>>.collectAndHandleState(
    handleLoadStates: (LoadStates, () -> Unit) -> Job
): LazyPagingItems<T> {
    val lazyPagingItem = collectAsLazyPagingItems()

    // Handle the different load state event
    val pagingLoadStates = lazyPagingItem.loadState.mediator ?: lazyPagingItem.loadState.source
    LaunchedEffect(pagingLoadStates) {
        handleLoadStates(pagingLoadStates)
    }

    return lazyPagingItem
}

要使用此扩展功能,请在内部进行组合:

// Get our paging items
val pagingItems = viewModel.cachedPagingItems.collectAndHandleState(viewModel::handleLoadState)
LazyColumn {
    item {
        // Display an error if any, or any informative message here, coming from an UiState exposed by you're view model
    } 

    // Display the items
    items(pagingItems) { pagedItem ->
        // The composable you want to display
    }


}

在你的视图模型中,一个简单的函数:

fun handleLoadState(loadStates: LoadStates, retry: () -> Unit): Job {
    // Handle the different load state, find the error or loading ones and update you're ui state
    // To rapidly find an error you can do : 
    val errorLoadState = arrayOf(
        loadStates.append,
        loadStates.prepend,
        loadStates.refresh
    ).filterIsInstance(LoadState.Error::class.java).firstOrNull()
    // And then check the error by getting the associated throwable
    val throwable = errorLoadState?.error

}

我在第 206 行的惰性行中处理了服务器错误 Github

有一种简单的方法可以找出发生了什么类型的错误,希望下面的代码对您有所帮助。

   events.apply {
                when {
                    /** Showing error state */
                    loadState.refresh is LoadState.Error -> {
                        val e = loadState.refresh as LoadState.Error
                        val message = ""
                        if (e.error is UnknownHostException) {
                            message = // Your message
                        } else if (e.error is /*Exception*/){
                            message = // Your message
                        }
                        item {
                            ErrorView(message)
                        }
                    }

                    /** Showing error state */
                    loadState.append is LoadState.Error -> {
                        val e = loadState.refresh as LoadState.Error
                        val message = ""
                        if (e.error is UnknownHostException) {
                            message = // Your message
                        } else if (e.error is /*Exception*/){
                            message = // Your message
                        }
                        item {
                            ErrorView(message)
                        }
                    }
                }
            }