Android 10 BadParcelableException ClassNotFoundException 使用 Kotlin 解组时出现 @Parcelize

问题描述

在 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(在我从事的市场中,这是由于低端设备导致的常见情况)

在我的应用中,我可以执行以下步骤:

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 时,我的应用程序崩溃(仅在启用 DNKA 的情况下),因为我没有正确处理 onSavedInstanceState

从日志(这里删除了不必要的东西)我可以看到:

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 崩溃并且 PlayStore 和 Firebase 只显示后来的崩溃? 2 - 我有什么建议可以改进以正确记录我的应用程序中的崩溃?

解决方法

此问题 was reported a year ago 用于您的第一个堆栈跟踪,该问题涉及 FragmentManagerState。它似乎与 Android 10 特定的错误有关。

对于您的 InfoState 崩溃,将对象作为 byte[] 放入包中应该可以解决问题。但是,对于 FragmentManagerState 而言,这不是您的选择,因为它正在 Jetpack 中更深入地完成。

您可能需要确认您使用的是最新的 androidx.fragmentandroidx.activity 库,以防他们添加了解决方法。否则,您可能会查看关于该问题的评论,因为一些开发人员报告了一些解决方案,但它们相当具体,可能与您的场景相关,也可能不相关。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...