FragmentStatePagerAdapter restore 的坑

1.背景

在內(nèi)存不足的手機(jī)上,某些非前臺(tái)頁(yè)面會(huì)因?yàn)閮?nèi)存不足而銷毀魂挂,此時(shí)再次進(jìn)入會(huì)執(zhí)行reconstruct 的邏輯,也就是 save 马昨、restore 邏輯,此時(shí)界面展示異常

用戶正在瀏覽大眾點(diǎn)評(píng)的團(tuán)購(gòu)詳情頁(yè),然后微信來了一條消息,此時(shí)打開微信,可能點(diǎn)評(píng)的團(tuán)購(gòu)詳情頁(yè)就被銷毀了

目的:不想要當(dāng)前 activity 保留狀態(tài)仓坞,銷毀后和重新進(jìn)入頁(yè)面保持一致

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);
        }

走一遍主流程后,發(fā)現(xiàn) listener 在回調(diào)的時(shí)候?yàn)榭?/p>

此時(shí)幸好使用了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();
        }
        //回調(diào)adapter的getItem方法
        Fragment fragment = getItem(position);
        //下面代碼省略
    }

在幾個(gè)關(guān)鍵節(jié)點(diǎn)(隨緣)打斷點(diǎn)后發(fā)現(xiàn)界面上展示的 fragment 不是生成的fragment,然后 debug adapter 的 getItem 方法毛雇,發(fā)現(xiàn)根本就沒有調(diào)用 getItem 方法嫉称,因?yàn)?mFragments 中有fragment
其實(shí)吧,上述代碼里面的注釋已經(jīng)寫得很清楚了

此時(shí)便基本確定不是代碼的問題灵疮,而是 FragmentStatePagerAdapter 源碼實(shí)現(xiàn)的問題了

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 時(shí)主要存儲(chǔ)了 mAdapter.saveState();
敲黑板 此時(shí)可以復(fù)習(xí)下 save 和 restore 的調(diào)用時(shí)機(jī)和注意點(diǎn),具體查看 onSaveInstanceState執(zhí)行時(shí)機(jī)

//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 存儲(chǔ)到 bundle 中震捣,然后通過 mFragmentManager.getFragment(bundle, key);來進(jìn)行 fragment 的恢復(fù)

如果有興趣荔棉,可以繼續(xù)看 Fragment.java-->void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {

4 .解決方式

復(fù)寫 FragmentStatePagerAdapter.saveState()

public class TestAdapter extends FragmentStatePagerAdapter {
    @Override
    public Parcelable saveState() {//空實(shí)現(xiàn),不傳數(shù)據(jù)
        return null;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蒿赢,一起剝皮案震驚了整個(gè)濱河市润樱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌羡棵,老刑警劉巖壹若,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異皂冰,居然都是意外死亡店展,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門秃流,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赂蕴,“玉大人,你說我怎么就攤上這事舶胀∷龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵峻贮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我应闯,道長(zhǎng)纤控,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任碉纺,我火速辦了婚禮船万,結(jié)果婚禮上刻撒,老公的妹妹穿的比我還像新娘。我一直安慰自己耿导,他們只是感情好声怔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舱呻,像睡著了一般醋火。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箱吕,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天芥驳,我揣著相機(jī)與錄音,去河邊找鬼茬高。 笑死兆旬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怎栽。 我是一名探鬼主播丽猬,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼熏瞄!你這毒婦竟也來了脚祟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤巴刻,失蹤者是張志新(化名)和其女友劉穎愚铡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胡陪,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沥寥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柠座。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邑雅。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妈经,靈堂內(nèi)的尸體忽然破棺而出淮野,到底是詐尸還是另有隱情,我是刑警寧澤吹泡,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布骤星,位于F島的核電站,受9級(jí)特大地震影響爆哑,放射性物質(zhì)發(fā)生泄漏洞难。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一揭朝、第九天 我趴在偏房一處隱蔽的房頂上張望队贱。 院中可真熱鬧色冀,春花似錦、人聲如沸柱嫌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)编丘。三九已至与学,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘪吏,已是汗流浹背癣防。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掌眠,地道東北人蕾盯。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蓝丙,于是被迫代替她去往敵國(guó)和親级遭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容