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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:08:32