Android 分页 RemoteMediator 未加载任何数据
Android paging RemoteMediator not loading any data
我正在尝试为从 Retrofit 服务器和 Room 数据库获取数据的 Jetpack Compose 应用程序设置 Paging 3。我创建了一个 RemoteMediator 来加载我想要的数据。我第一次 运行 该应用程序时,它正确显示了项目列表。但是,任何后续运行都不会显示任何数据,即使数据在服务器上也是如此。我想让数据显示在屏幕上。
经过检查,数据似乎已从 Room 数据库中删除,并且不再调用 RemoteMediator 加载方法。因此可组合项中显示的项目数为 0。我不知道是什么阻止了加载方法的调用。
远程调解器:
@OptIn(ExperimentalPagingApi::class)
class TaskArchiveRemoteMediator(
private val clock: Clock,
private val promptDatabase: PromptDatabase,
private val promptService: PromptService,
) : RemoteMediator<Int, Task>() {
// override suspend fun initialize(): InitializeAction {
// val lastRefreshedAt = promptDatabase.taskDao().getLastArchiveRefreshedAt()
// return if (lastRefreshedAt?.let { isExpired(it, Instant.now(clock)) } == false) {
// Log.d("TaskArchiveRemoteMediator", "Should not refresh")
// InitializeAction.SKIP_INITIAL_REFRESH
// } else {
// Log.d("TaskArchiveRemoteMediator", "Should refresh")
// InitializeAction.LAUNCH_INITIAL_REFRESH
// }
// }
override suspend fun load(loadType: LoadType, state: PagingState<Int, Task>): MediatorResult {
return try {
Log.d("TaskArchiveRemoteMediator", "loadType $loadType")
val loadKey = when (loadType) {
LoadType.REFRESH -> null
LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
LoadType.APPEND -> {
val remoteKey = getRemoteKeyForLastItem(state)
remoteKey?.nextPageKey
?: return MediatorResult.Success(endOfPaginationReached = true)
}
}
val limit = if (loadType == LoadType.REFRESH) {
state.config.initialLoadSize
} else {
state.config.pageSize
}
val page = getTaskPage(loadKey, limit)
Log.d("TaskArchiveRemoteMediator", "page: $page")
promptDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
promptDatabase.taskDao().deleteArchive()
promptDatabase.taskRemoteKeyDao().deleteAll()
}
page.tasks.lastOrNull()?.let {
promptDatabase.taskRemoteKeyDao().insert(TaskRemoteKey(it.id, page.nextCursor))
}
val tasks = page.tasks.map { it.toTask(Instant.now(clock)) }
Log.d("TaskArchiveRemoteMediator", "tasks: $tasks")
promptDatabase.taskDao().insertAll(tasks)
}
MediatorResult.Success(endOfPaginationReached = page.nextCursor == null)
} catch (e: TaskArchivePagingException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
} catch (e: IOException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
} catch (e: HttpException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Task>) =
state.lastItemOrNull()?.id?.let { promptDatabase.taskRemoteKeyDao().getById(it) }
private suspend fun getTaskPage(after: String?, size: Int): TaskPage {
val response = promptService.getArchive(after, size)
if (!response.isSuccessful) {
throw TaskArchivePagingException(response.message())
}
return response.body() ?: throw TaskArchivePagingException("Server response has empty body")
}
}
存储库:
class TaskArchiveRepository @Inject constructor(
private val clock: Clock,
private val promptDatabase: PromptDatabase,
private val promptService: PromptService,
) {
@OptIn(ExperimentalPagingApi::class)
fun getArchive(pageSize: Int) =
Pager(
PagingConfig(pageSize),
remoteMediator = TaskArchiveRemoteMediator(clock, promptDatabase, promptService),
) {
promptDatabase.taskDao().getArchive()
}.flow
}
可组合:
@Composable
fun TaskArchiveScreen(
setTopBarState: (TopBarState) -> Unit = {},
navigateToTaskDetails: (String) -> Unit = {},
taskArchiveViewModel: TaskArchiveViewModel = hiltViewModel(),
) {
val taskItems = rememberWithLifecycle(taskArchiveViewModel.archive).collectAsLazyPagingItems()
val hasData = taskItems.loadState.refresh is LoadState.NotLoading && taskItems.itemCount > 0
Log.d("TaskArchiveScreen", "loadState: ${taskItems.loadState}, itemCount: ${taskItems.itemCount}")
val overflowActions = if (hasData) {
val refreshAction = ActionState(
icon = painterResource(R.drawable.ic_baseline_refresh_24),
title = stringResource(R.string.refresh_action),
onClick = taskItems::refresh,
)
listOf(refreshAction)
} else {
emptyList()
}
setTopBarState(
TopBarState(
title = stringResource(R.string.task_archive_title),
navIconType = NavIconType.Menu,
overflowActions = overflowActions,
)
)
when {
taskItems.loadState.refresh is LoadState.Loading -> TaskListLoadingContent()
taskItems.loadState.refresh is LoadState.Error ->
LoadFailureContent(
loadFailureMessage = stringResource(R.string.load_task_archive_failure_message),
reload = taskItems::retry,
)
hasData ->
TaskArchiveDataContent(
taskItems = taskItems,
navigateToTaskDetails = navigateToTaskDetails,
)
else -> TaskArchiveEmptyContent()
}
}
如何加载我的分页项?
原来来自服务器的数据格式不正确:(
我正在尝试为从 Retrofit 服务器和 Room 数据库获取数据的 Jetpack Compose 应用程序设置 Paging 3。我创建了一个 RemoteMediator 来加载我想要的数据。我第一次 运行 该应用程序时,它正确显示了项目列表。但是,任何后续运行都不会显示任何数据,即使数据在服务器上也是如此。我想让数据显示在屏幕上。
经过检查,数据似乎已从 Room 数据库中删除,并且不再调用 RemoteMediator 加载方法。因此可组合项中显示的项目数为 0。我不知道是什么阻止了加载方法的调用。
远程调解器:
@OptIn(ExperimentalPagingApi::class)
class TaskArchiveRemoteMediator(
private val clock: Clock,
private val promptDatabase: PromptDatabase,
private val promptService: PromptService,
) : RemoteMediator<Int, Task>() {
// override suspend fun initialize(): InitializeAction {
// val lastRefreshedAt = promptDatabase.taskDao().getLastArchiveRefreshedAt()
// return if (lastRefreshedAt?.let { isExpired(it, Instant.now(clock)) } == false) {
// Log.d("TaskArchiveRemoteMediator", "Should not refresh")
// InitializeAction.SKIP_INITIAL_REFRESH
// } else {
// Log.d("TaskArchiveRemoteMediator", "Should refresh")
// InitializeAction.LAUNCH_INITIAL_REFRESH
// }
// }
override suspend fun load(loadType: LoadType, state: PagingState<Int, Task>): MediatorResult {
return try {
Log.d("TaskArchiveRemoteMediator", "loadType $loadType")
val loadKey = when (loadType) {
LoadType.REFRESH -> null
LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
LoadType.APPEND -> {
val remoteKey = getRemoteKeyForLastItem(state)
remoteKey?.nextPageKey
?: return MediatorResult.Success(endOfPaginationReached = true)
}
}
val limit = if (loadType == LoadType.REFRESH) {
state.config.initialLoadSize
} else {
state.config.pageSize
}
val page = getTaskPage(loadKey, limit)
Log.d("TaskArchiveRemoteMediator", "page: $page")
promptDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
promptDatabase.taskDao().deleteArchive()
promptDatabase.taskRemoteKeyDao().deleteAll()
}
page.tasks.lastOrNull()?.let {
promptDatabase.taskRemoteKeyDao().insert(TaskRemoteKey(it.id, page.nextCursor))
}
val tasks = page.tasks.map { it.toTask(Instant.now(clock)) }
Log.d("TaskArchiveRemoteMediator", "tasks: $tasks")
promptDatabase.taskDao().insertAll(tasks)
}
MediatorResult.Success(endOfPaginationReached = page.nextCursor == null)
} catch (e: TaskArchivePagingException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
} catch (e: IOException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
} catch (e: HttpException) {
Log.e("TaskArchiveRemoteMediator", "archive load error", e)
MediatorResult.Error(e)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Task>) =
state.lastItemOrNull()?.id?.let { promptDatabase.taskRemoteKeyDao().getById(it) }
private suspend fun getTaskPage(after: String?, size: Int): TaskPage {
val response = promptService.getArchive(after, size)
if (!response.isSuccessful) {
throw TaskArchivePagingException(response.message())
}
return response.body() ?: throw TaskArchivePagingException("Server response has empty body")
}
}
存储库:
class TaskArchiveRepository @Inject constructor(
private val clock: Clock,
private val promptDatabase: PromptDatabase,
private val promptService: PromptService,
) {
@OptIn(ExperimentalPagingApi::class)
fun getArchive(pageSize: Int) =
Pager(
PagingConfig(pageSize),
remoteMediator = TaskArchiveRemoteMediator(clock, promptDatabase, promptService),
) {
promptDatabase.taskDao().getArchive()
}.flow
}
可组合:
@Composable
fun TaskArchiveScreen(
setTopBarState: (TopBarState) -> Unit = {},
navigateToTaskDetails: (String) -> Unit = {},
taskArchiveViewModel: TaskArchiveViewModel = hiltViewModel(),
) {
val taskItems = rememberWithLifecycle(taskArchiveViewModel.archive).collectAsLazyPagingItems()
val hasData = taskItems.loadState.refresh is LoadState.NotLoading && taskItems.itemCount > 0
Log.d("TaskArchiveScreen", "loadState: ${taskItems.loadState}, itemCount: ${taskItems.itemCount}")
val overflowActions = if (hasData) {
val refreshAction = ActionState(
icon = painterResource(R.drawable.ic_baseline_refresh_24),
title = stringResource(R.string.refresh_action),
onClick = taskItems::refresh,
)
listOf(refreshAction)
} else {
emptyList()
}
setTopBarState(
TopBarState(
title = stringResource(R.string.task_archive_title),
navIconType = NavIconType.Menu,
overflowActions = overflowActions,
)
)
when {
taskItems.loadState.refresh is LoadState.Loading -> TaskListLoadingContent()
taskItems.loadState.refresh is LoadState.Error ->
LoadFailureContent(
loadFailureMessage = stringResource(R.string.load_task_archive_failure_message),
reload = taskItems::retry,
)
hasData ->
TaskArchiveDataContent(
taskItems = taskItems,
navigateToTaskDetails = navigateToTaskDetails,
)
else -> TaskArchiveEmptyContent()
}
}
如何加载我的分页项?
原来来自服务器的数据格式不正确:(