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

线上部分页面报ViewModel类型转换异常 求排查解决方案

问题描述

我们多个页面均继承了BasePagingFragment类,但线上环境仅部分页面出现Object cannot be cast to androidx.lifecycle.ViewModel异常,未开启混淆时无此报错,目前无法定位确切原因,需排查解决。

相关代码

BasePagingFragment.kt

//BasePagingFragment.kt
abstract class BasePagingFragment<T : Any, Vm : BasePagingViewModel<T>> :
    BaseViewFragment<BaseFragmentPagingListBinding>(R.layout.base_fragment_paging_list) {
...other code
    open fun createOpenViewModel(): Vm {
        return createViewModel(getViewmodelKey())
    }
...other code
}

ViewTop.kt

//ViewTop.kt
fun <VM : BasePagingViewModel<*>> BasePagingFragment<*, VM>.createViewModel(key: String? = null): VM {
    val vmClazz1: Class<VM> = getVmClazz1(this)
    val newKey = key ?: "com.paging" + vmClazz1.javaClass.simpleName
    return ViewModelProvider(
        this,
        ViewModelProvider.AndroidViewModelFactory(this.requireActivity().application)
    ).get(newKey, vmClazz1)
}

inline fun <reified VM> getVmClazz1(obj: Any): VM {
    val genericSuperclass = obj.javaClass.genericSuperclass
    return (genericSuperclass as ParameterizedType).actualTypeArguments[1] as VM
}
排查与解决方案

核心原因:混淆导致泛型类型信息丢失

开启混淆后,ProGuard/R8会对类名、泛型参数等进行混淆或擦除,而你的代码依赖运行时泛型类型信息获取ViewModel的Class对象,这就是问题根源:

  • getVmClazz1方法通过obj.javaClass.genericSuperclass获取父类泛型参数,但混淆后部分子类的泛型类型信息被擦除,导致actualTypeArguments[1]返回的不是预期的Class<VM>,最终ViewModelProvider.get()返回的对象无法转成目标VM类型,抛出类型转换异常。
  • 仅部分页面出现问题,是因为不同页面的子类结构或混淆规则的细微差异,导致部分子类泛型信息被保留,部分被擦除。

解决步骤

1. 临时方案:保留泛型类型信息

在混淆规则文件(proguard-rules.pro)中添加规则,确保相关类的泛型信息不被擦除:

# 保留BasePagingFragment子类的泛型与类结构
-keep class com.yourpackage.** extends com.yourpackage.BasePagingFragment {
    <fields>;
    <methods>;
}
# 保留BasePagingViewModel子类的类结构
-keep class com.yourpackage.** extends com.yourpackage.BasePagingViewModel {
    <fields>;
    <methods>;
}
# 强制保留泛型、内部类等签名信息
-keepattributes Signature, InnerClasses

替换com.yourpackage为项目实际包名。

2. 彻底方案:重构ViewModel获取逻辑(摆脱泛型依赖)

依赖运行时泛型获取Class对象本身就不稳定,建议改为显式传入Class对象的方式:
修改BasePagingFragment,让子类必须提供ViewModel的Class:

abstract class BasePagingFragment<T : Any, Vm : BasePagingViewModel<T>> :
    BaseViewFragment<BaseFragmentPagingListBinding>(R.layout.base_fragment_paging_list) {
    
    // 子类实现此方法返回对应ViewModel的Class
    protected abstract fun getViewModelClass(): Class<Vm>

    open fun createOpenViewModel(): Vm {
        return createViewModel(getViewmodelKey(), getViewModelClass())
    }
}

修改createViewModel方法:

fun <VM : BasePagingViewModel<*>> BasePagingFragment<*, VM>.createViewModel(
    key: String? = null,
    vmClass: Class<VM>
): VM {
    val newKey = key ?: "com.paging" + vmClass.simpleName
    return ViewModelProvider(
        this,
        ViewModelProvider.AndroidViewModelFactory(this.requireActivity().application)
    ).get(newKey, vmClass)
}

这种方式彻底摆脱了对运行时泛型信息的依赖,从根源上解决混淆导致的类型转换问题。

3. 验证

开启混淆打包后,测试所有页面是否正常运行,确认异常不再出现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:47:24