[Android]ViewPager+Fragment重建泄漏问题解决

Android组件化架构

我是苍王,以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。
[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

近来遇到一个比较奇怪的Fragment问题
app很多时候会使用Viewpager+Fragments的方式显示布局,特别是在主页。
如果app在栈内已经打开了几个Activity了,突然之间发生崩溃,一般情况下崩溃被捕抓后,会重新恢复试着重新恢复栈顶的崩溃前的Activity。
对于这种崩溃情况,Activity毫无疑问崩溃前会走onSaveInstanceState函数,恢复时会走onRestoreInstanceState恢复场景。
但是当包含ViewPager+Fragments的Activity被触发恢复后,你会发现竟然这些Fragments会有内存泄漏的情况。
分析:
这个非常不容易发现,使用Profile查看app对象的时候,会发现这些Fragments会被创建了两次,这种情况是无法通过LeakCanary来侦查到的,因为是主的Activity做出的泄漏。
而且顶上显示的Fragment并不是Activity所持有的Fragments,如果是某些数据是从Activity中获取的,将会无法显示。
原因:
翻阅了源码,我们可以看到以下的FragmentActivity恢复的代码和重建FragmentActivity的onCreate流程

 /**
     * Save all appropriate fragment state.
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //记录哪些Fragments被创建了
        markFragmentsCreated();
        //保存Fragments状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        ……
    }

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            //栈中的Fragments恢复状态
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
       ……  
        }

        mFragments.dispatchCreate();
    }

这里可以看到奔溃的时候mFragments的会在onSaveInstanceState被记录已经创建了,并且会被调用了saveAllState,然后重建时onCreate会调用restoreAllState,恢复Fragment,然后重新创建一次Fragments,一般情况下super.onCreate会在子类调用之前执行,这个时候Fragments的恢复会比子类Activity onCreate要前。
然后重新走onCreate流程,然后重新创建ViewPager和Fragments。那么Fragments就会被创建两次了。
解决方法:
无法知道在恢复的mFragments的TAG标志,所以Activity无法通过TAG获取。

1.在onSaveInstanceState的时候,不调用super的方法,直接调用FragmentActivity.onStateNotSaved(),直接截断mFragments会恢复栈。但这样onSaveInstanceState就会无法恢复状态,有可能有未知的错误,所以不采纳
2.在onCreate的流程的时候,先判断supportFragmentManager.fragments是否有对应的Fragment,然后如果存在就不创建多次,直接在supportFragmentManager.fragments中获取。

失败方案,对supportFragmentManager.fragments清空是无效的,supportFragmentManager.fragments是不等价于FragmentActivity.mFragments的

这里还要谨记两点,
1.onRestoreInstanceState生命周期,是在onCreate之后,在onResume之前的,一些通过恢复的操作,只能在onResume中进行。
2.Application.LifeRecyleCallback无法监听到onRestoreInstanceState的执行。


Android组件化群2