Android 10中使用Kotlin @Parcelize进行反序列化时出现BadParcelableException ClassNotFoundException。

5
在 Firebase 中,我在我的生产应用程序(使用 Dexguard)中使用 Parcelable 时遇到了各种崩溃问题。 这些崩溃现在影响到近200个用户,并分为Firebase上的3个条目。 在一项中,崩溃仅出现在三星设备上;在另一个中,仅出现在小米设备上;在第三项中,来自更多品牌的用户遇到问题,但其中81%来自HMD Global。 所有这些设备都运行 Android 10,因此这可能是该操作系统版本的问题。我可以看到这些崩溃只涉及5或6个自定义对象。除此之外,一些崩溃与以下相关:
Fatal Exception: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: androidx.fragment.app.FragmentManagerState
   at android.os.Parcel.readParcelableCreator(Parcel.java:3059)
   at android.os.Parcel.readParcelable(Parcel.java:2981)
   at android.os.Parcel.readValue(Parcel.java:2883)
   at android.os.Parcel.readArrayMapInternal(Parcel.java:3261)
   at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
   at android.os.BaseBundle.unparcel(BaseBundle.java:236)
   at android.os.BaseBundle.size(BaseBundle.java:355)
   at android.app.servertransaction.LaunchActivityItem.hashCode(LaunchActivityItem.java:208)
   at java.util.AbstractList.hashCode(AbstractList.java:541)
   at java.util.Objects.hashCode(Objects.java:98)
   at android.app.servertransaction.ClientTransaction.hashCode(ClientTransaction.java:241)
   at android.app.servertransaction.TransactionExecutorHelper.tId(TransactionExecutorHelper.java:266)
   at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:86)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2220)
   at android.os.Handler.dispatchMessage(Handler.java:107)
   at android.os.Looper.loop(Looper.java:237)
   at android.app.ActivityThread.main(ActivityThread.java:8019)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

在我的一个自定义对象中,我遇到了这种崩溃问题(对于我的其他自定义对象,崩溃问题也是相同的)。

Fatal Exception: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.mypackagename.InfoData
       at android.os.Parcel.readParcelableCreator(Parcel.java:3059)
       at android.os.Parcel.readParcelable(Parcel.java:2981)
       at android.os.Parcel.readValue(Parcel.java:2883)
       at android.os.Parcel.readArrayMapInternal(Parcel.java:3261)
       at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
       at android.os.BaseBundle.unparcel(BaseBundle.java:236)
       at android.os.BaseBundle.size(BaseBundle.java:355)
       at android.app.servertransaction.LaunchActivityItem.hashCode(LaunchActivityItem.java:208)
       at java.util.AbstractList.hashCode(AbstractList.java:541)
       at java.util.Objects.hashCode(Objects.java:98)
       at android.app.servertransaction.ClientTransaction.hashCode(ClientTransaction.java:241)
       at android.app.servertransaction.TransactionExecutorHelper.tId(TransactionExecutorHelper.java:266)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:86)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2220)
       at android.os.Handler.dispatchMessage(Handler.java:107)
       at android.os.Looper.loop(Looper.java:237)
       at android.app.ActivityThread.main(ActivityThread.java:8019)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

作为一个例子,这里有一个我遇到崩溃问题的模型示例。
@Parcelize
class NumberDetails(internal var numbers: ArrayList<String> = arrayListOf(), var userState: UserState = UserState.INACTIVE) : Parcelable {

    fun clear() = numbers.clear()

    fun getNumbers(): String = numbers.joinToString("")
}

另一个:

@Parcelize
data class InfoData(var needsCheck: Boolean = false, var amount: Double = 0.0, var fee: Double? = null) : Parcelable

还有一个:

abstract class Screen(open val identifier: String = "") : Parcelable {

    @Parcelize
    data class ActivateScreen(override val identifier: String) : Screen()


   //More entries like above
 }

第一个示例是内部类,而第二个示例是在我已经有其他内容的文件中声明的(不知道这是否重要)。
我无法在任何设备上复制崩溃情况。 你有什么想法为什么只有在Android 10设备上出现这些崩溃?
更新: 在尝试修复我的应用程序崩溃时,我能够再现FragmentManagerState崩溃,也就是说,我的应用程序崩溃了,然后再次崩溃了,并且发生了FragmentManagerState崩溃。虽然我可以始终复制我的应用程序崩溃,但我只能一次复制后续崩溃。
这在运行Android 10的模拟器中以及启用"Don't Keep Activities"时被复制。由于低端设备的常见情况,在我工作的市场上,启用"Don't Keep Activities"。
在我的应用程序中,我可以执行以下步骤:
ActivityA-> ActivityB-> ActivityC-> ActivityD(PdfViewer)-> PickActivity(I / ActivityTaskManager:START u0 {act = android.intent.action.CREATE_DOCUMENT cat = [android.intent.category.OPENABLE] typ = application/pdf cmp = com.android.documentsui/ .picker.PickActivity)
ActivityD中,我可以预览PDF的位置,我可以单击一个按钮将PDF保存到存储中。单击该按钮会打开系统PickActivity。当我保存PDF并将控件传递给ActivityC时,如果我没有正确处理onSavedInstanceState,我的应用程序会崩溃(仅在启用DNKA的情况下)。
从日志中(这里删除了不必要的内容),我可以看到:
I/ActivityTaskManager: START u0 {act=android.intent.action.CREATE_DOCUMENT cat=[android.intent.category.OPENABLE] typ=application/pdf cmp=com.android.documentsui/.picker.PickActivity (has extras)} from uid 10219
I/ActivityManager: Start proc 9418:com.android.documentsui/u0a50 for activity {com.android.documentsui/com.android.documentsui.picker.PickActivity}
I/ActivityManager: Start proc 9443:com.android.externalstorage/u0a56 for content provider {com.android.externalstorage/com.android.externalstorage.ExternalStorageProvider}
I/ActivityTaskManager: Displayed com.android.documentsui/.picker.PickActivity: +1s133ms
2016-3548/? I/ActivityTaskManager: START u0 {cmp=com.myapp.packagename/com.myapp.package.ActivityC} from uid 10219
2016-2323/? I/ActivityTaskManager: START u0 {cmp=com.myapp.packagename/com.myapp.package.ActivityC} from uid 10219

然后我的应用程序由于未正确处理保存的实例状态而崩溃

W/ActivityTaskManager: Activity top resumed state loss timeout for ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250}
W/ActivityTaskManager: Activity pause timeout for ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250}
W/ActivityTaskManager:   Force finishing activity com.myapp.packagename/com.myapp.package.ActivityC
W/ActivityTaskManager:   Force finishing activity com.myapp.packagename/com.myapp.package.ActivityC
I/ActivityManager: Process com.myapp.packagename (pid 8990) has died: fore TOP 
W/ActivityTaskManager: Force removing ActivityRecord{4b658e u0 com.myapp.packagename/com.myapp.package.ActivityB t1250}: app died, no saved state
W/ActivityTaskManager: Force removing ActivityRecord{210b4e5 u0 com.myapp.packagename/com.myapp.package.Activitya t1250}: app died, no saved state
I/ActivityManager: Start proc 9609:com.myapp.packagename/u0a219 for activity {com.myapp.packagename/com.myapp.package.ActivityD}
W/ActivityTaskManager: Activity top resumed state loss timeout for ActivityRecord{5d7ded3 u0 com.myapp.packagename/com.myapp.package.ActivityC t-1 f}

现在我可以通过日志看到我的应用程序被启动了(来自我的应用程序类的日志)

I/ActivityTaskManager: START u0 {flg=0x10008000 cmp=com.myapp.packagename/com.myapp.package.SplashActivity} from uid 10219
I/ActivityTaskManager: START u0 {cmp=com.myapp.packagename/com.myapp.package.SplashActivity} from uid 10219
 W/ActivityTaskManager: startActivity called from finishing ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250 f}; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: Intent { cmp=com.myapp.packagename/com.myapp.package.SplashActivity }
W/ActivityTaskManager: Duplicate finish request for ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250 f}
W/ActivityTaskManager: Duplicate finish request for ActivityRecord{685f9ab u0 com.myapp.packagename/com.myapp.package.ActivityC t1250 f}
W/ActivityTaskManager: Activity top resumed state loss timeout for ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250 f}
W/ActivityTaskManager: Activity pause timeout for ActivityRecord{bdaedc1 u0 com.myapp.packagename/com.myapp.package.ActivityD t1250 f}

然后:

9609-9609/com.myapp.packagename E/Parcel: Class not found when unmarshalling: androidx.fragment.app.FragmentManagerState
    java.lang.ClassNotFoundException: androidx.fragment.app.FragmentManagerState

在分析日志时,我发现我的其中一个活动出现了两次。我通过快速多次点击启动该活动的按钮来重现它。之后,我尝试复制这个问题,并重新启动流程,双击按钮,试图保存PDF,在应用崩溃后,我可以看到FragmentManagerState崩溃。我只能够重现这个问题一次。
1 - 由于所有崩溃都与BadParcelableException有关(来自FragmentManagerState和其他事物),结合上述内容,我在Play商店看到的崩溃是否被标记为BadParcelableException,实际上是与我的应用程序中的其他问题相关的崩溃,也会触发BadParcelableException崩溃,但Play商店和Firebase只显示后者? 2 - 是否有任何建议可以改进我的应用程序以正确记录崩溃?

你可能需要扩展你的[mcve]以显示生成崩溃的每个位置的完整堆栈跟踪。通常情况下,在某些情况下,自定义Parcelable对象具有风险 - CommonsWare
@CommonsWare 谢谢。我无法重现错误,但已经在问题中编辑了两个堆栈跟踪。 因此,看了您的帖子,您是否建议使用Matthias Urhahn的解决方法并执行SO答案中提供的技术?如果是这样,我需要在我的端上进行一些重构/替换,但这比崩溃要好。 - Favolas
1个回答

1

这个问题一年前就已经报告了,是关于第一个堆栈跟踪的,它涉及到一个与Android 10相关的bug。

对于你的InfoState崩溃,将对象作为byte[]放入bundle中应该可以解决问题。但对于FragmentManagerState来说,这不是一个选择,因为它是在Jetpack更深层次上完成的。

你可能需要确认一下是否使用了最新的androidx.fragmentandroidx.activity库,以防它们添加了一个解决方法。否则,你可能需要查看该问题的评论,因为一些开发者报告了一些解决方案,但它们相当具体,可能与你的情况有关,也可能没有。


谢谢。我会检查我的依赖版本,并从重构开始 :( - Favolas
1
@Favolas:我可能以错误的顺序编写了这些解决方案。查看问题并查看是否有任何描述的情况(例如,最近删除了singleTop)与您的情况相匹配。如果是这样,那么该修复程序可能已足够。跟踪依赖关系是一件相当标准的事情。将您的“InfoState”转换为“byte []”可能有所帮助,但是可能其他解决方案可以在不必走这条路的情况下澄清问题。 - CommonsWare
1
很遗憾,我没有singleTop问题。我会继续调查。谢谢。 - Favolas
你好。仍然遇到崩溃问题,但找到了一些东西。请检查我的更新并看看是否有意义?谢谢。 - Favolas
1
@Favolas:抱歉,我不知道如何回答你的两个新问题。 - CommonsWare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接