FragmentStatePagerAdapter restore 的坑

1.背景

在内存不足的手机上,某些非前台页面会因为内存不足而销毁,此时再次进入会执行reconstruct 的逻辑,也就是 save 、restore 逻辑,此时界面展示异常

用户正在浏览大众点评的团购详情页,然后微信来了一条消息,此时打开微信,可能点评的团购详情页就被销毁了

目的:不想要当前 activity 保留状态,销毁后和重新进入页面保持一致

2.问题探索

//伪代码如下
 for (int i = 0, len = Math.min(xx.size(), TAB_MAX_COUNT); i < len; i++) {
            Fragment fragment = new Fragment()
            fragment.setListener(listener);
            mFragments.add(fragment);
        }

走一遍主流程后,发现 listener 在回调的时候为空

此时幸好使用了kotlin,避免了一次明显的空指针异常,线上没有 crash
@Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        //回调adapter的getItem方法
        Fragment fragment = getItem(position);
        //下面代码省略
    }

在几个关键节点(随缘)打断点后发现界面上展示的 fragment 不是生成的fragment,然后 debug adapter 的 getItem 方法,发现根本就没有调用 getItem 方法,因为 mFragments 中有fragment
其实吧,上述代码里面的注释已经写得很清楚了

此时便基本确定不是代码的问题,而是 FragmentStatePagerAdapter 源码实现的问题了

3. 源码查看

//Viewpager.java
@Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.position = mCurItem;
        if (mAdapter != null) {
            ss.adapterState = mAdapter.saveState();
        }
        return ss;
    }
    
     @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (mAdapter != null) {
            mAdapter.restoreState(ss.adapterState, ss.loader);
            setCurrentItemInternal(ss.position, false, true);
        } else {
            mRestoredCurItem = ss.position;
            mRestoredAdapterState = ss.adapterState;
            mRestoredClassLoader = ss.loader;
        }
    }

由代码可知,Viewpager 在 save 时主要存储了 mAdapter.saveState();
敲黑板 此时可以复习下 save 和 restore 的调用时机和注意点,具体查看 onSaveInstanceState执行时机

//FragmentStatePagerAdapter.java
   @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i=0; i keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }

saveState 主要是将 fragment.mIndex 存储到 bundle 中,然后通过 mFragmentManager.getFragment(bundle, key);来进行 fragment 的恢复

如果有兴趣,可以继续看 Fragment.java-->void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {

4 .解决方式

复写 FragmentStatePagerAdapter.saveState()

public class TestAdapter extends FragmentStatePagerAdapter {
    @Override
    public Parcelable saveState() {//空实现,不传数据
        return null;
    }
}

你可能感兴趣的:(FragmentStatePagerAdapter restore 的坑)