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

Epoxy更新已构建模型属性触发ImmutableModelException崩溃该如何解决

崩溃根因

Epoxy的核心设计要求模型一旦完成构建加入控制器队列后就是不可修改的,任何事后修改属性的操作都会直接抛出ImmutableModelException,你的代码存在三个触发崩溃的问题:

  • 模型ID使用随机值:每次调用requestModelBuild()重新构建模型时,都给CountryItemModel_赋值随机UUID作为ID,Epoxy无法通过ID关联新旧模型,无法正常执行差量更新逻辑,反而会认为是完全不同的新模型,大幅提升冲突概率。
  • 使用可变集合作为EpoxyAttribute:listOfPreSelectedCountryFilters声明为MutableList,Epoxy做属性一致性校验时仅校验对象引用是否变更,如果你外部修改了该MutableList的内容,即使引用不变,内容变更也会被Epoxy检测到,触发不可变模型异常。
  • 直接修改已生成的模型属性:Interceptor回调里拿到的models是已经完成构建的模型实例,此时修改属性属于违规操作,直接触发异常。
修复方案

1. 替换随机ID为稳定唯一值

使用业务唯一标识作为模型ID,保证同一个业务项的ID在多次buildModels时保持一致,同时传入集合时转成不可变类型:

override fun buildModels() {
    sortFilterViewState.let { sortFilterViewState ->
        sortFilterViewState.filterTypes?.forEach { topsProductFilter ->
            when (SortFilterType.getId(topsProductFilter.attributeCode)) {
                SortFilterType.COUNTRY -> {
                    CountryItemModel_()
                        // 替换为业务唯一ID,比如topsProductFilter的id或者attributeCode
                        .id(topsProductFilter.attributeCode)
                        .tapCountryChipRelay(tapCountryRelay)
                        .countryFilter(topsProductFilter)
                        // 转成不可变集合避免外部修改影响校验
                        .listOfPreSelectedCountryFilters(sortFilterViewState.listOfCurrentlySelectedCountryItems?.toList() ?: emptyList())
                        .addTo(this)
                }
            }
        }
    }
}

2. EpoxyAttribute使用不可变类型

把模型类里的MutableList改成只读的List,避免外部修改集合内容触发校验失败:

@EpoxyModelClass(layout = R.layout.list_item_country_item)
abstract class CountryItemModel : EpoxyBaseModel() {

    @EpoxyAttribute
    lateinit var tapCountryChipRelay: PublishRelay<TopsProductFilter>

    @EpoxyAttribute
    lateinit var countryFilter: TopsProductFilter

    // 改为不可变List
    @EpoxyAttribute
    lateinit var listOfPreSelectedCountryFilters: List<TopsProductFilterItem>

    override fun bind(holder: EpoxyBaseViewHolder) {
        with(holder.itemView) {
        // 原有逻辑不变
      }
   }
}

3. 移除违规修改逻辑,仅通过状态更新触发重绘

删除Interceptor相关代码,onTapClearAll里只要更新控制器的状态变量即可,setter内部已经调用了requestModelBuild,Epoxy会自动根据新状态构建新模型、执行差量更新:

private fun onTapClearAll() {
    // 直接赋值新状态即可,不需要额外操作
    sortFilterController.sortFilterViewState = sortFilterViewState
    sortFilterController.sortFilterType = SortFilterType.ALL    
}

可选优化

如果SortFilterViewState不是Kotlin数据类,需要手动重写equals和hashCode方法,保证属性变更时能被Epoxy正确识别,提升差量更新的性能和准确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 11:15:03