疑难问题:
ClassNotFoundException when unmarshalling: 问题分析
问题描述
-----------------------Crash 1 Message-----------------------
完整log:
02-20 21:08:32.727 E/AndroidRuntime(32306): FATAL EXCEPTION: main
02-20 21:08:32.727 E/AndroidRuntime(32306): Process: , PID: 32306
02-20 21:08:32.727 E/AndroidRuntime(32306): : ClassNotFoundException when unmarshalling:
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:3042)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:2964)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:2866)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:3244)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:292)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:236)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:355)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:208)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:541)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:98)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:241)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:266)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:86)
02-20 21:08:32.727 E/AndroidRuntime(32306): at $(:2016)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:107)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:214)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:7356)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (Native Method)
02-20 21:08:32.727 E/AndroidRuntime(32306): at $(:492)
02-20 21:08:32.727 E/AndroidRuntime(32306): at (:930)
这个异常来自于Bugly。从去年6月的8820版本就开始存在。 日崩溃量差不多在150左右,是个历史疑难问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2Xyrtx5-1616553788110)(/secure/attachment/353350/)]
从Bugly的出错堆栈,跟踪数据,跟踪日志等维度上都没有找到有用的信息。只知道奔溃发生在主线程,而且App的使用时长 1-3s。也就是说通常是
Application一启动就崩溃了,还没有进入到Activity的页面初始化。
通过Google分析了下这个问题:
- 该异常表示对Parcelable数据进行反序列化时找不到相关类。
2.这种报错有一种原因是与Android的classloader机制有关。Android有两种不同的classloaders:framework classloader和Apk classloader,应用启动时默认的classloader是Apk classloader,可以加载Parcelable反序列化所需的类。当内存不足时,默认classloader将变为framework classloader,它不知道如何加载自己定义的类,因而会报错。
通常的解决方法是:在解组数据前,加上(getClass().getClassLoader()),将恢复Apk classloader方式。
但是这个异常的Parcelable 数据是在FragmentManagerState里面序列化的,这个数据里面保存的是Activity页面中的Fragment数据, 这个数据并不是我们手动序列化,而是App退到后台的时候,使用onSaveInstanceState 自动帮我们序列化的。是系统的行为。
序列化的时机是:#onSaveInstanceState(, )
反序列化的时机是:是#onCreate()
因此可以大概推测出这个问题的发生路径:
-
APP切换到后台,当进入后台比较久或者内存不够的时候,APP被杀死。
-
再次切换APP到前台,这时候安卓系统会根据 杀死APP 的时候 onSaveInstanceState 方法中保存的Fragment信息,重构页面。但是这时候 使用的是Framework classLoader, 会找不到这个Fragment,导致无法Activity无法构建成功,导致崩溃。
更深入分析发现,这异常只会发生在 Android 10 的版本,而且 Android 官方Issue那边有条相关的反馈
issue的 状态的 wont fix。 官方的回复是这个问题 只会发生在 Android 10 预览版。 安卓10 已经修复了这个问题,建议厂商升级软件版本
解决
问题已经比较清晰了,那么就可以尝试解决了。 由于这个异常是发生在Activity#onCreate 中,因此,我们需要在 Activity基类中拦截这个异常,同时为了减少对其他版本和手机的影响,限制了系统版本并缩小了机型范围。
//Top5易发生的机器
private val deviceModel = arrayOf("REDMI 8A", "V1962A", "V1986A", "PDPM00", "V1938CT")
private fun condition(): Boolean {
val deviceMode = DeviceUtils.getDeviceMode()
//只处理安卓10机器
val versionEqual = Build.VERSION.SDK_INT == Build.VERSION_CODES.Q
return versionEqual && deviceModel.contains(deviceMode)
}
fun intercept(context: Context, bundle: Bundle?) {
if (bundle != null && condition()) {
bundle.classLoader = context.classLoader
}
}
//GlideMemoryOptimizeActivity Activity基类中
@Override
protected void onCreate(Bundle savedInstanceState) {
FixFragmentHelper.INSTANCE.intercept(this,savedInstanceState);
super.onCreate(savedInstanceState);
...
}
- Jira链接
- bugly