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

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

首先說(shuō)一個(gè)題外話,周五測(cè)試提了一個(gè)bug,跳至下一個(gè)Fragment,再回到之前的fragment,之前對(duì)一個(gè)textview做的操作都沒(méi)了。

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

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

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

  1. Fragment多了一對(duì)onCreateView/onDestroyView,大概也是因?yàn)镕ragment相比Activity,更多的是做View展示。
  2. Fragment多了onAttach和onDetach,用于標(biāo)識(shí)跟Activity連接。
  3. start/stop(可見(jiàn)),resume/pause(在前臺(tái))這兩對(duì)是共有的,activity有個(gè)特殊的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);  
   } 

那就要注意,如果你覆寫這個(gè)新的方法,當(dāng)你的gradle中的target api小于23的時(shí)候,onAttach就不會(huì)被調(diào)用了(上周接入外部SDK的時(shí)候由于他們的so不支持targetApi23以上,所以我們的app改成了targetApi22。這就是風(fēng)險(xiǎn))。正確的做法是升級(jí)targetApi,或者同時(shí)寫兩個(gè)onAttach。

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

我們組的App里用的是UIData的方案,我覺(jué)得很巧妙;在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對(duì)象并沒(méi)有被GC回收,所以mActivity并沒(méi)有被回收。

另外,對(duì)于ViewPager+Fragment這樣的設(shè)計(jì),viewpager默認(rèn)情況當(dāng)你滑到第三頁(yè)的時(shí)候,第一頁(yè)的Fragment就會(huì)onDestroyView,這時(shí)候再回來(lái)第一頁(yè),很多東西都會(huì)重建了。這種情形要分情況,如果是實(shí)時(shí)性高的app,這么做無(wú)可厚非;如果是實(shí)時(shí)性低的app,那可以在onCreateView里判斷網(wǎng)絡(luò)數(shù)據(jù)是否為空,或者rootView是否為空,如果不為空就不請(qǐng)求網(wǎng)絡(luò),避免資源消耗。

Activity的狀態(tài)保存

我們的Activity會(huì)有一個(gè)public的mUidata,在onCreate(),onRestoreInstanceState()和onRestoreInstanceState()的時(shí)候,都會(huì)把它從savedInstanceState中存放/取出。

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

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

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

我看了下這個(gè)文章,提到了Handler,廣播/EventBus,接口,setArgument等方式;其中最后一種的setArgument方式是Fragment的設(shè)計(jì)師們的初衷,我之前也簡(jiǎn)單分析過(guò),它的優(yōu)勢(shì)是可以在Activity重建的時(shí)候恢復(fù)數(shù)據(jù),也正因?yàn)槿绱耍現(xiàn)ragment不推薦使用單例。相比之下我感覺(jué)我們組的UIData方案還是蠻好的。

View的狀態(tài)保存

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

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

我們的JDRView中確實(shí)繼承了它,用于在保存和恢復(fù)的時(shí)候給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的時(shí)候調(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的時(shí)候都會(huì)給mUidata賦值,然后initUi/updateUi。分別用來(lái)從磁盤上恢復(fù)緩存數(shù)據(jù)和View被重建的時(shí)候恢復(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時(shí)updateUI。
                ((JDRView) cardView).loadData(null);
            }
        }
    }

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評(píng)論 6 540
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,275評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書人閱讀 177,904評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,633評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,368評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,736評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,919評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,481評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,235評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,427評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,656評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 35,055評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,348評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,160評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,380評(píng)論 2 379

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