java.lang.RuntimeException: Parcelable encountered IOException reading a Serializable object

工作中遇到一个Crash log的分析过程:

Log相关的Message:

关键1:“java.lang.RuntimeException: Parcelable encountered IOException reading a Serializable object”

关键2: caused by java.io.InvalidClassException : cannot bind enum descriptor to a non-enum class

java.lang.RuntimeException : Unable to start activity ComponentInfo{com.xxxx.mobile/com.xxxx.ui.home.HomeActivity}: java.lang.RuntimeException: Parcelable encountered IOException reading a Serializable object (name = com.xxxx.ui.home.navigation.a.h)
 caused by java.lang.RuntimeException : Parcelable encountered IOException reading a Serializable object (name = com.xxxx.ui.home.navigation.a.h)
0	android.os.Parcel.readSerializable (Parcel.java : 3153)
1	android.os.Parcel.readValue (Parcel.java : 2934)
2	android.os.Parcel.readArrayMapInternal (Parcel.java : 3261)
3	android.os.BaseBundle.initializeFromParcelLocked (BaseBundle.java : 292)
4	android.os.BaseBundle.unparcel (BaseBundle.java : 236)
5	android.os.BaseBundle.getInt (BaseBundle.java : 1035)
6	android.content.Intent.getIntExtra (Intent.java : 8487)
7	com.xxxx.uikit.base.activity.AbstractBaseActivity.handleIntent (AbstractBaseActivity.java : 392)
8	com.xxxx.uikit.base.activity.AbstractBaseActivity.onCreate (AbstractBaseActivity.java : 95)
9	com.xxxx.ui.home.AbstractBaseNavActivity.onCreate (AbstractBaseNavActivity.java : 36)
10	com.xxxx.ui.home.HomeActivity.onCreate (HomeActivity.java : 130)
android.app.Activity.performCreate (Activity.java : 7963) - com.android.internal.os.ZygoteInit.main (ZygoteInit.java : 1100)
 caused by java.io.InvalidClassException : cannot bind enum descriptor to a non-enum class
0	java.io.ObjectStreamClass.initNonProxy (ObjectStreamClass.java : 614)
1	java.io.ObjectInputStream.readNonProxyDesc (ObjectInputStream.java : 1713)
2	java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java : 1594)
3	java.io.ObjectInputStream.readEnum (ObjectInputStream.java : 1824)
4	java.io.ObjectInputStream.readObject0 (ObjectInputStream.java : 1409)
5	java.io.ObjectInputStream.readObject (ObjectInputStream.java : 427)
6	android.os.Parcel.readSerializable (Parcel.java : 3151)
7	android.os.Parcel.readValue (Parcel.java : 2934)
8	android.os.Parcel.readArrayMapInternal (Parcel.java : 3261)
9	android.os.BaseBundle.initializeFromParcelLocked (BaseBundle.java : 292)
10	android.os.BaseBundle.unparcel (BaseBundle.java : 236)
11	android.os.BaseBundle.getInt (BaseBundle.java : 1035)
12	android.content.Intent.getIntExtra (Intent.java : 8487)
13	com.xxxx.uikit.base.activity.AbstractBaseActivity.handleIntent (AbstractBaseActivity.java : 392)
14	com.xxxx.uikit.base.activity.AbstractBaseActivity.onCreate (AbstractBaseActivity.java : 95)
15	com.xxxx.ui.home.AbstractBaseNavActivity.onCreate (AbstractBaseNavActivity.java : 36)
16	com.xxxx.ui.home.HomeActivity.onCreate (HomeActivity.java : 130)
17  android.app.Activity.performCreate (Activity.java : 7963) - com.android.internal.os.ZygoteInit.main (ZygoteInit.java : 1100)

分析这个Crash Log的时候,起初我认为是由于onSaveInstanceState / onRestoreInstanceState中的代码问题导致,可是看了下代码中,并没有可能导致Crash的问题代码。

于是将关注点放在了第二个提示Log上。

为什么会提示将一个非枚举类转成枚举类的错误?

查看代码发现,在Intent.putExtra中,我们确实存放了一个枚举类对象的值,进行Intent的数据传递。

虽然枚举类对象不建议通过这种方式传递,但是其实看起来也并无大碍才对。

那么紧接着观察到关键Log 1中的类名:name = com.xxxx.ui.home.navigation.a.h,想到有可能是用户升级app新版本的时候,这个枚举类混淆后,名字已经变掉了。

对比查看了两个old 和 new 的版本,发现的确在升级后,由于这个包名下我们增加了一个新的类,导致混淆后com.xxxx.ui.home.navigation.a.h对应了另一个非枚举类了,而原先版本的枚举类名则变成了:com.xxxx.ui.home.navigation.a.m

那么直到了这个原因是由于这个混淆导致的,那么为什么呢?

理论上来说升级完后,所有的名字应该都是按照混淆后的名字来处理,混淆后变成了com.xxxx.ui.home.navigation.a.m就应该是这个,而不应该是com.xxxx.ui.home.navigation.a.h的。

那么居然有这个情况的出现,我们大胆地猜测了,应该是在apk升级过程中的某个时间点,dex中的类替换了原先的版本中的类,可是程序运行过程中用户点击了Notification 或者 其他deep link的方式发送了intent / pendingIntent进入了app,导致intent使用了这个枚举类对象数据传递了,从而导致了错误。

我画了一个简单的时间线,大致如下:

System upgrade app timeline 

v1.0  Update to v2.0 start ----------- Class replace done ---------------------------------------------- Update end,kill app, notification clear.

User operation timeline

Receive notification(v1.0) ---------  Class replace done -- Click on the previous notification -- Crash -- Update end,kill app

简单写了一个Python脚本,通过adb的方式去反复模拟用户更新过程,然后点击deep link进入app,发现确实可以复现,概率大概在3/10左右。

记录一下这个Bug,如果有其他人遇到,可以参考下这个情况是否会发生。

综上,总结注意的点:
1.Intent传递尽量传递基础类型参数(像上面,单反是个Object都是有可能出这个crash的)
2.传递的参数类型,可以不混淆的,就不要混淆了。
3.不要使用枚举类型做参数传递,不然版本更新,有可能之前存的枚举类型在新版本上没有,也会导致类找不到的问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值