You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose类别数据缓存问题:重复点击避免重复请求

最优临时数据缓存方案(Jetpack Compose)

核心思路

用ViewModel内的内存缓存存储已加载的类别图片数据——ViewModel生命周期与页面绑定,能在页面存活期间保留缓存,且不会因配置变更丢失。推荐用MutableMap<String, List<Image>>(假设Image是你的图片数据类)按类别Key存储对应数据,请求前先查缓存,存在则直接返回,不存在再发起网络请求。

代码实现示例

ViewModel层

class GalleryViewModel(private val repository: GalleryRepository) : ViewModel() {
    // 内存缓存:类别ID -> 对应图片列表
    private val categoryImageCache = mutableMapOf<String, List<Image>>()
    private val _imagesState = MutableStateFlow<UiState<List<Image>>>(UiState.Idle)
    val imagesState: StateFlow<UiState<List<Image>>> = _imagesState

    fun loadImagesByCategory(categoryId: String) {
        // 先检查缓存,存在则直接返回
        categoryImageCache[categoryId]?.let { cachedImages ->
            _imagesState.value = UiState.Success(cachedImages)
            return
        }

        // 缓存不存在,发起网络请求
        viewModelScope.launch {
            _imagesState.value = UiState.Loading
            runCatching {
                repository.getImagesByCategory(categoryId)
            }.onSuccess { images ->
                // 将新数据写入缓存
                categoryImageCache[categoryId] = images
                _imagesState.value = UiState.Success(images)
            }.onFailure {
                _imagesState.value = UiState.Error(it.message ?: "加载失败")
            }
        }
    }

    // 通用UI状态密封类
    sealed class UiState<out T> {
        object Idle : UiState<Nothing>()
        object Loading : UiState<Nothing>()
        data class Success<out T>(val data: T) : UiState<T>()
        data class Error(val message: String) : UiState<Nothing>()
    }
}

Compose Screen层

@Composable
fun GalleryScreen(viewModel: GalleryViewModel = viewModel()) {
    val imagesState by viewModel.imagesState.collectAsState()
    var selectedCategory by remember { mutableStateOf("default") }

    Column {
        // 类别选择LazyRow
        LazyRow {
            items(categories) { category ->
                CategoryItem(
                    category = category,
                    isSelected = category.id == selectedCategory,
                    onClick = {
                        selectedCategory = category.id
                        viewModel.loadImagesByCategory(category.id)
                    }
                )
            }
        }

        // 图片展示LazyColumn
        when (imagesState) {
            GalleryViewModel.UiState.Idle -> Text("请选择类别")
            GalleryViewModel.UiState.Loading -> CircularProgressIndicator()
            is GalleryViewModel.UiState.Success -> {
                LazyColumn {
                    items((imagesState as GalleryViewModel.UiState.Success).data) { image ->
                        ImageItem(image = image)
                    }
                }
            }
            is GalleryViewModel.UiState.Error -> {
                Text((imagesState as GalleryViewModel.UiState.Error).message)
            }
        }
    }
}

进阶优化方案

如果需要限制缓存大小或管理更多数据,可改用LruCache替代普通Map:

// 在ViewModel中初始化LruCache
private val imageCache = LruCache<String, List<Image>>(
    maxSize = 5 * 1024 * 1024 // 5MB,可根据实际数据体积调整
)

// 读取缓存
imageCache.get(categoryId)?.let { cachedImages ->
    _imagesState.value = UiState.Success(cachedImages)
    return
}

// 写入缓存
imageCache.put(categoryId, images)

常见问题规避

  • 旧数据问题:每次写入缓存时必须用最新网络请求结果覆盖对应Key的缓存,禁止追加或复用旧数据。
  • 空列表问题:若网络请求返回空列表,也要写入缓存,避免重复请求;如需区分“无数据”和“未请求”,可在UiState中新增Empty状态。
  • 配置变更问题:ViewModel自带的内存缓存会在旋转屏幕等配置变更时自动保留,无需额外处理。

内容的提问来源于stack exchange,提问作者AhmadAB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 07:52:50