Android Paging Library与整洁架构集成的映射问题咨询
解决整洁架构中Paging Library的模型映射问题
看起来你在整洁架构下集成Paging 2时遇到了两个核心问题:一是PagedList本身不支持直接映射,二是当前架构中领域层依赖了Android框架的PagedList类(违反了整洁架构中领域层独立于框架的原则)。下面给你几个可行的解决方案:
方案一:在Adapter绑定阶段动态处理展示层状态
这是最简单的方案,不需要修改现有PagedList的流转逻辑。因为选中状态属于展示层的UI状态,不需要提前嵌入到模型中,而是可以在Adapter绑定ViewHolder时动态获取:
1. ViewModel维护选中状态
class ItemViewModel(private val repo: ItemRepository) : ViewModel() { // 用LiveData维护选中的Item ID集合 private val _selectedItemIds = MutableLiveData<Set<Int>>(emptySet()) val selectedItemIds: LiveData<Set<Int>> = _selectedItemIds // 暴露领域层的PagedList val items: Observable<PagedList<Item>> = repo.getItems() fun toggleSelection(itemId: Int) { val current = _selectedItemIds.value ?: emptySet() _selectedItemIds.value = if (current.contains(itemId)) current - itemId else current + itemId } }
2. Adapter绑定动态判断选中状态
class ItemAdapter(private val viewModel: ItemViewModel) : PagedListAdapter<Item, ItemViewHolder>(DiffCallback) { init { // 监听选中状态变化,刷新对应项 viewModel.selectedItemIds.observeForever { // 可选:优化为只刷新状态变化的项,这里先简化为刷新所有可见项 notifyDataSetChanged() } } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { val item = getItem(position) ?: return val isSelected = viewModel.selectedItemIds.value?.contains(item.id) ?: false holder.bind(item, isSelected) } }
这种方式避免了PagedList的映射问题,同时符合整洁架构的职责划分:领域层只负责提供业务数据,展示层负责维护UI状态并绑定到视图。
方案二:调整架构,在展示层完成PagedList的模型映射
如果确实需要将领域模型转换为展示层模型(比如除了选中状态还有其他UI专属属性),你需要先修复领域层依赖Android框架的问题,再在展示层完成映射:
1. 修改领域层仓库接口,避免依赖PagedList
领域层应该是纯Kotlin模块,不能依赖Android的PagedList。改为返回DataSource.Factory(Paging 2的核心组件),由数据层实现时将数据实体映射为领域模型:
领域层仓库接口
interface ItemRepository { fun getItemDataSourceFactory(): Observable<DataSource.Factory<Int, Item>> }
数据层实现
class ItemRepositoryImpl(private val dataSourceFactory: ItemEntityDataSourceFactory) : ItemRepository { override fun getItemDataSourceFactory(): Observable<DataSource.Factory<Int, Item>> { // 数据层将实体映射为领域模型 val mappedFactory = dataSourceFactory.map { entity -> Item(entity.id, entity.name) } return Observable.just(mappedFactory) } }
2. ViewModel中完成展示层映射
class ItemViewModel(private val repo: ItemRepository) : ViewModel() { private val selectedItemIds = mutableSetOf<Int>() val uiItems: Observable<PagedList<ItemUiModel>> = repo.getItemDataSourceFactory() .map { itemFactory -> // 展示层将领域模型映射为UI模型 itemFactory.map { item -> ItemUiModel(item.id, item.name, selectedItemIds.contains(item.id)) } } .flatMap { uiFactory -> val config = PagedList.Config.Builder() .setPageSize(10) .setInitialLoadSizeHint(15) .setPrefetchDistance(5) .setEnablePlaceholders(false) .build() RxPagedListBuilder(uiFactory, config) .setInitialLoadKey(1) .buildObservable() } fun toggleSelection(itemId: Int) { if (selectedItemIds.contains(itemId)) selectedItemIds.remove(itemId) else selectedItemIds.add(itemId) // 刷新PagedList:可通过重新创建Observable或触发DataSource失效实现 } }
方案三:升级到Paging 3(推荐)
Paging 3彻底重构了API,使用PagingData(纯Kotlin类)替代PagedList,完美适配整洁架构的领域层(纯Kotlin模块)。它支持直接通过map操作符转换模型,示例如下:
领域层仓库接口
interface ItemRepository { fun getItems(): Flow<PagingData<Item>> }
数据层实现
class ItemRepositoryImpl(private val pagingSource: ItemPagingSource) : ItemRepository { override fun getItems(): Flow<PagingData<Item>> { return Pager( config = PagingConfig(pageSize = 10, initialLoadSize = 15), pagingSourceFactory = { pagingSource } ).flow.map { pagingData -> // 数据层转领域模型 pagingData.map { entity -> Item(entity.id, entity.name) } } } }
ViewModel中转换为UI模型
class ItemViewModel(private val repo: ItemRepository) : ViewModel() { private val _selectedItemIds = MutableStateFlow<Set<Int>>(emptySet()) val selectedItemIds = _selectedItemIds.asStateFlow() val uiItems: Flow<PagingData<ItemUiModel>> = repo.getItems() .combine(selectedItemIds) { pagingData, selectedIds -> pagingData.map { item -> ItemUiModel(item.id, item.name, selectedIds.contains(item.id)) } } .cachedIn(viewModelScope) fun toggleSelection(itemId: Int) { _selectedItemIds.update { current -> if (current.contains(itemId)) current - itemId else current + itemId } } }
Paging 3的API更简洁,且天然支持模型映射和状态结合,是整洁架构下的最优选择。
内容的提问来源于stack exchange,提问作者Marco Pierucci
相关产品推荐
相关产品推荐

