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正式版通過此方案進行了修復,修復失敗
方案二:
解決思路就是處理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完美解決問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM