美文网首页
FragmentStatePagerAdapter restor

FragmentStatePagerAdapter restor

作者: 温温开水 | 来源:发表于2018-07-10 14:24 被阅读10次

    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<mFragments.size(); i++) {
                Fragment f = mFragments.get(i);
                if (f != null && f.isAdded()) {
                    if (state == null) {
                        state = new Bundle();
                    }
                    String key = "f" + i;
                    mFragmentManager.putFragment(state, key, f);
                }
            }
            return state;
        }
        
         @Override
        public void restoreState(Parcelable state, ClassLoader loader) {
            if (state != null) {
                Bundle bundle = (Bundle)state;
                bundle.setClassLoader(loader);
                Parcelable[] fss = bundle.getParcelableArray("states");
                mSavedState.clear();
                mFragments.clear();
                if (fss != null) {
                    for (int i=0; i<fss.length; i++) {
                        mSavedState.add((Fragment.SavedState)fss[i]);
                    }
                }
                Iterable<String> 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 restor

          本文链接:https://www.haomeiwen.com/subject/opifpftx.html