關(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ū)別血筑,我目前感受:
- Fragment多了一對onCreateView/onDestroyView绘沉,大概也是因?yàn)镕ragment相比Activity,更多的是做View展示豺总。
- Fragment多了onAttach和onDetach车伞,用于標(biāo)識跟Activity連接。
- 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
這個類也是有onSaveInstanceState
和onRestoreInstanceState(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