ClassNotFoundException when unmarshalling: androidx.fragment.app.FragmentManagerState 神奇的 bug 修复之路

 

崩溃栈信息:

java.lang.RuntimeException: Unable to start activity ComponentInfo{global.longbridge.app.android/com.longbridge.wealth.mvp.ui.activity.WealthWithdrawActivity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: androidx.fragment.app.FragmentManagerState
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3894)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4077)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2458)
at com.longbridge.common.tracker.apm.hook.b.handleMessage(ProxyHandlerCallback.java:42)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8387)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: androidx.fragment.app.FragmentManagerState
at android.os.Parcel.readParcelableCreator(Parcel.java:3042)
at android.os.Parcel.readParcelable(Parcel.java:2964)
at android.os.Parcel.readValue(Parcel.java:2866)
at android.os.Parcel.readArrayMapInternal(Parcel.java:3244)
at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
at android.os.BaseBundle.unparcel(BaseBundle.java:236)
at android.os.Bundle.getParcelable(Bundle.java:951)
at androidx.fragment.app.FragmentActivity$2.onContextAvailable(FragmentActivity.java:148)
at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:297)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:273)
at me.imid.swipebacklayout.lib.app.SwipeBackActivity.onCreate(SwipeBackActivity.java:17)
at com.longbridge.common.base.FBaseActivity.onCreate(FBaseActivity.java:85)
at com.longbridge.wealth.mvp.ui.activity.WealthWithdrawActivity.onCreate(Unknown Source:8)
at android.app.Activity.performCreate(Activity.java:8121)
at android.app.Activity.performCreate(Activity.java:8109)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1320)
at com.qiyukf.unicorn.l.a.callActivityOnCreate(QiyuInstrumentation.java:258)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3867)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4077)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2458)
at com.longbridge.common.tracker.apm.hook.b.handleMessage(ProxyHandlerCallback.java:42)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8387)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)

崩溃原因剖析:

用户打开了使用到ViewPager+Fragment的页面后,长时间放置在后台当系统内存不足时被系统回收,Fragment的一些信息会在Activity调用onSaveInstanceState把这些信息通过Bundle的方式暂存起来,用户再次唤起此页面时会从暂存的savedInstanceState中尝试恢复页面,在此过程中 Android 10的系统中的BaseBundle.unparcel会抛出异常导致崩溃。
是一个系统的bug,这异常只会发生在 Android 10 的版本,而且 Android 官方 Issue 那边有条相关的反馈,issue的 状态的 wont fix。 官方的回复是这个问题只会发生在 Android 10 预览版。 安卓 10 已经修复了这个问题,建议厂商升级软件版本。

尝试的修复方案:

方案一:
https://blog.csdn.net/GYBIN02/article/details/115165448
通过替换classLoader的方式进行修复: bundle.classLoader = context.classLoader
修复结果:3.10.0正式版通过此方案进行了修复,修复失败
方案二:
错误处理Class not found when unmarshalling: android.support.v4.app.FragmentManagerState_GGGL的专栏-CSDN博客
解决思路就是处理savedInstanceState,不让它恢复android:support:fragments的内容.
修复结果:通过热修复的方式下发了修复补丁,修复失败
方案三:
对指定页面的onCreate函数进行try catch容错处理,如果抛异常了,直接finish。
修复结果:修复成功
方案四:
通过Hook的方式对发现崩溃的地方就行try catch,目前还在寻找hook方案
遇见的难题:系统代码无法hook,只能对引入的库进行hook,目前在想办法尝试对FragmentActivity进行hook处理。
修复结果:仍在寻找方案
方案五:
问题基本就定位到了,其实就是在activity重建时,传入的savedInstanceState本身ClassLoader是没问题的,问题出在它的子Bundle的子Bundle上,层级结构如下:
savedInstanceState.getBundle("androidx.lifecycle.BundlableSavedStateRegistry.key").getBundle("android:support:fragment")
问题是出在这个bundle的ClassLoader上,这里在android 10和9的机器上使用的是BootClassLoader,然后在解parcel的时候就崩溃了。
 
 
 @JvmStatic
    fun intercept(context: Context?, bundle: Bundle?) {
        if (bundle == null) return
        val condition = condition()
        if (context != null && condition) {
            bundle.classLoader = context.javaClass.classLoader
            // 需要修改的是bundle下androidx.lifecycle.BundlableSavedStateRegistry.key中子项的classloader
            bundle.getBundle("androidx.lifecycle.BundlableSavedStateRegistry.key")?.let {
                it.keySet()?.forEach { key ->
                    (it.get(key) as? Bundle)?.classLoader = context.javaClass.classLoader
                }
            }
        }
    }

  

总结:
  采用方案5完美解决问题
posted on 2021-10-09 15:56  总李写代码  阅读(1308)  评论(2编辑  收藏  举报