线上部分页面报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
相关产品推荐
相关产品推荐

