Activity/Fragment/View的狀態(tài)保存/數(shù)據(jù)恢復(fù)

關(guān)于Activity/Fragment的生命周期

首先說一個題外話囊蓝,周五測試提了一個bug饿悬,跳至下一個Fragment,再回到之前的fragment聚霜,之前對一個textview做的操作都沒了狡恬。

于是我又去看了下View的lifecircle,發(fā)現(xiàn)跟Activity還是很不一樣的蝎宇,他從backstack中取出上一個fragment之后會走onCreateView弟劲,而inflateview和butterknifer bind view都是在onCreateView中做的(這是對的,onCreateView要返回view的函卒,官方文檔也說這個view里要返回這個fragment的root layout。onCreate的話撇眯,就是初始化必要組件用的)报嵌,也就是說view是被重建了的,所以view狀態(tài)都變成了初始狀態(tài)熊榛。

最終我是這么解決的這個業(yè)務(wù)問題的:在onCreateView的update當(dāng)前fragment的view的方法里加了判斷锚国,如果uidata中相應(yīng)的數(shù)據(jù)有,我就把textview更新玄坦。

Activity和Fragment生命周期的區(qū)別血筑,我目前感受:

  1. Fragment多了一對onCreateView/onDestroyView绘沉,大概也是因?yàn)镕ragment相比Activity,更多的是做View展示豺总。
  2. Fragment多了onAttach和onDetach车伞,用于標(biāo)識跟Activity連接。
  3. start/stop(可見)喻喳,resume/pause(在前臺)這兩對是共有的另玖,activity有個特殊的onRestart。

另外表伦,注意到onAttach(Activity activity)在api 23以上已經(jīng)deprecated了谦去,參數(shù)變了:

/* 
* onAttach(Context) is not called on pre API 23 versions of Android and onAttach(Activity) is deprecated 
* Use onAttachToContext instead 
*/  
   @TargetApi(23)  
   @Override  
   public void onAttach(Context context) {  
       super.onAttach(context);  
       onAttachToContext(context);  
   } 

那就要注意,如果你覆寫這個新的方法蹦哼,當(dāng)你的gradle中的target api小于23的時候鳄哭,onAttach就不會被調(diào)用了(上周接入外部SDK的時候由于他們的so不支持targetApi23以上,所以我們的app改成了targetApi22纲熏。這就是風(fēng)險)妆丘。正確的做法是升級targetApi,或者同時寫兩個onAttach局劲。

關(guān)于Fragment的狀態(tài)保存

我們組的App里用的是UIData的方案飘痛,我覺得很巧妙;在Fragment的onAttach()方法(另外容握,onCreate()方法里也把getActivity賦給mActivity)做了mActivity的恢復(fù)和mUiData的恢復(fù):

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.mActivity = (CPActivity) activity;
        this.mUIData = mActivity.mUIData;
    }

這樣可以避免getActivity由于onDetach造成的空指針宣脉。因?yàn)閛nDetach之后,getActivity就是null了剔氏;但是其實(shí)Fragment對象并沒有被GC回收塑猖,所以mActivity并沒有被回收。

另外谈跛,對于ViewPager+Fragment這樣的設(shè)計羊苟,viewpager默認(rèn)情況當(dāng)你滑到第三頁的時候,第一頁的Fragment就會onDestroyView感憾,這時候再回來第一頁蜡励,很多東西都會重建了。這種情形要分情況阻桅,如果是實(shí)時性高的app凉倚,這么做無可厚非;如果是實(shí)時性低的app嫂沉,那可以在onCreateView里判斷網(wǎng)絡(luò)數(shù)據(jù)是否為空稽寒,或者rootView是否為空,如果不為空就不請求網(wǎng)絡(luò)趟章,避免資源消耗杏糙。

Activity的狀態(tài)保存

我們的Activity會有一個public的mUidata慎王,在onCreate(),onRestoreInstanceState()和onRestoreInstanceState()的時候宏侍,都會把它從savedInstanceState中存放/取出赖淤。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 加載數(shù)據(jù)
        if (savedInstanceState == null) {
//initUIData是抽象方法,子類實(shí)現(xiàn)
            mUIData = initUIData();
        } else {
//記得這里我還問過谅河,為什么要重新set一次classLoader咱旱,領(lǐng)導(dǎo)解釋了,我大概憶起是classLoader在恢復(fù)的時候可能發(fā)生錯亂       
            savedInstanceState.setClassLoader(getClass().getClassLoader());
            mUIData = (UIData) savedInstanceState.getSerializable(UIDATA);
        }
        super.onCreate(savedInstanceState);

        imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    }

Activity的Fragment之間的數(shù)據(jù)通信

我看了下這個文章旧蛾,提到了Handler,廣播/EventBus蠕嫁,接口锨天,setArgument等方式;其中最后一種的setArgument方式是Fragment的設(shè)計師們的初衷剃毒,我之前也簡單分析過病袄,它的優(yōu)勢是可以在Activity重建的時候恢復(fù)數(shù)據(jù),也正因?yàn)槿绱俗阜В現(xiàn)ragment不推薦使用單例益缠。相比之下我感覺我們組的UIData方案還是蠻好的。

View的狀態(tài)保存

不光Activity基公,View這個類也是有onSaveInstanceStateonRestoreInstanceState(android.os.Parcelable)這對保存state的方法的幅慌。
對于一個復(fù)雜view,比如一個用戶填滿了的表單轰豆,如果用戶旋轉(zhuǎn)了下屏幕胰伍,數(shù)據(jù)就都沒了肯定不行。

View中有個類酸休,View.BaseSavedState骂租,解釋是Base class for derived classes that want to save and restore their own onSaveInstanceState()。我們自定義地去保存View的state斑司。

我們的JDRView中確實(shí)繼承了它渗饮,用于在保存和恢復(fù)的時候給uidata賦值,也必須要往parcel里添加和恢復(fù)uidata:

    public static class SavedState extends BaseSavedState {
        /**
         * 保存的數(shù)據(jù)
         */
        private UIData mUIData;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            mUIData = (UIData) in.readSerializable();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeSerializable(mUIData);
        }

        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {

            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

我回想了一下JDRView是怎么恢復(fù)數(shù)據(jù)的宿刮?
JDRView的構(gòu)造函數(shù)
JDRView()->init(){
mUIData = initUIData();
initUI();}
在onRestoreSavedInstace的時候調(diào)用updateUI();

以FinanceView為例互站,

    @Override
    public UIData initUIData() {
        InvestmentListData investmentListData = mStorageUtil.get(InvestmentListData.class);
        if (investmentListData == null || investmentListData.fundCardInfo == null) {
            investmentListData = new InvestmentListData();
            investmentListData.fundCardInfo = createDefaultData();
        }
        return investmentListData;
    }

我們用的是mStorageUtil,作用是把Serializable的數(shù)據(jù)轉(zhuǎn)化成Json然后保存的SharedPreferences里去僵缺;在構(gòu)造函數(shù)初始化和onRestoreSavedInstance的時候都會給mUidata賦值云茸,然后initUi/updateUi。分別用來從磁盤上恢復(fù)緩存數(shù)據(jù)和View被重建的時候恢復(fù)數(shù)據(jù)谤饭。

MainSequenceFragment:

    protected void updateCardListUI(CardSequenceList cardSequenceList) {
        if (cardSequenceList == null) {
            return;
        }
        List<CardViewInfo> mCardList = cardSequenceList.tabStructureList;
        if (needDraw(mCardList)) {
            mCardView = CardViewManager.generateCardView(mActivity, mCardList);
            addCardViewLayout();
        }
        for (int i = 0; i < mCardView.size(); i++) {
            View cardView = mCardView.get(i);
            if (cardView instanceof JDRView) {
                //loadData利用cardId去取server數(shù)據(jù)标捺,onSuccess時updateUI懊纳。
                ((JDRView) cardView).loadData(null);
            }
        }
    }

cardId除了在緩存的時候有用,還有什么用亡容。從前似乎是傳頁面名稱的嗤疯,現(xiàn)在改成cardId了。

ref:
http://www.reibang.com/p/662c46cd3b5f
http://blog.csdn.net/u012702547/article/details/47151001
http://www.bubuko.com/infodetail-828889.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闺兢,一起剝皮案震驚了整個濱河市茂缚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屋谭,老刑警劉巖脚囊,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桐磁,居然都是意外死亡悔耘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門我擂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衬以,“玉大人,你說我怎么就攤上這事校摩】淳” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵衙吩,是天一觀的道長互妓。 經(jīng)常有香客問我,道長坤塞,這世上最難降的妖魔是什么车猬? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮尺锚,結(jié)果婚禮上珠闰,老公的妹妹穿的比我還像新娘。我一直安慰自己瘫辩,他們只是感情好伏嗜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伐厌,像睡著了一般承绸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挣轨,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天军熏,我揣著相機(jī)與錄音,去河邊找鬼卷扮。 笑死荡澎,一個胖子當(dāng)著我的面吹牛均践,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摩幔,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼彤委,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了或衡?” 一聲冷哼從身側(cè)響起焦影,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤冈欢,失蹤者是張志新(化名)和其女友劉穎氧急,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帖旨,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坡疼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年彬呻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片回梧。...
    茶點(diǎn)故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡废岂,死狀恐怖祖搓,靈堂內(nèi)的尸體忽然破棺而出狱意,到底是詐尸還是另有隱情,我是刑警寧澤拯欧,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布详囤,位于F島的核電站,受9級特大地震影響镐作,放射性物質(zhì)發(fā)生泄漏藏姐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一该贾、第九天 我趴在偏房一處隱蔽的房頂上張望羔杨。 院中可真熱鬧,春花似錦杨蛋、人聲如沸兜材。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曙寡。三九已至,卻和暖如春寇荧,著一層夾襖步出監(jiān)牢的瞬間举庶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工揩抡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留户侥,地道東北人镀琉。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像添祸,于是被迫代替她去往敵國和親滚粟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評論 2 350

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