談談Fragment的建構函式、重新建立(recreate)及相關

小飛_Xiaofei發表於2015-05-12

版權所有。所有權利保留。

歡迎轉載,轉載時請註明出處:

http://blog.csdn.net/xiaofei_it/article/details/45675497


本文分享一些Android Fragment使用經驗,不準備詳細介紹,只是介紹一些使用注意點,對於有一定開發經驗的朋友應該會比較有幫助。對於我理解不對的地方,希望批評指正,謝謝!


1、Fragment初始化

一定要提供預設建構函式。

不能用建構函式傳遞引數!不要寫帶引數的建構函式。引數通過下面介紹的方式傳遞。

原因:Fragment會被重新銷燬(Activity銷燬的時候它裡面的Fragment就被銷燬了,可能因為記憶體不足,手機配置發生變化,橫豎屏切換)。在重新建立的時候系統呼叫的是無參建構函式。

標準做法是:

在Fragment裡新增獲取Fragment的newInstance函式,以後獲取Fragment就使用這個函式,不要使用建構函式新建Fragment!

public static MyFragment newInstance(Bundle args) {
	MyFragment f = new MyFragment();
	f.setArguments(args);
	return f;
}

Fragment內部在初始化的時候需要獲取外界傳遞的引數,這時候就用getArguments獲取Bundle,再從Bundle裡獲取對應的引數。Bundle在Fragment銷燬和重新建立的時候持續儲存。

比如:

private TextView mTextView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
	View view=inflater.inflate(R.layout.fragment, container, false);
	initViews(view);
        return view;
}

private void initViews(View view){
	mTextView = view.findViewById(R.id.tv);
	Bundle bundle = getArguments();
	mTextView.setText(bundle == null ? "" : bundle.getString("text"));
}

Activity裡使用MyFragment時應該這樣寫:
Bundle args = new Bundle();
args.putString("text","Hello");
MyFragment f = MyFragment.newInstance(args)


其實還是有點小問題,具體見第5點。

2、關於getActivity()

Fragment的方法getActivity()只在被關聯到Activity之後才會得到結果,也就是在onAttach和onDetach兩個生命週期之間會非空(此時isAdded返回值是true)。其他時候不應該使用Activity!如果要使用,那說明你設計得不好。

使用所依附的Activity時應該判斷getActivity是否為空或者isAdded是否為true。

Fragment依附的Activity隨時可能被destroy掉!很多時候是在不經意間。機型適配的時候就會發現。


3、Activity引用

Fragment裡不應該保留Activity引用!需要用到的時候通過getActivity()獲取,因為那個引用不僅會導致記憶體洩露,而且在你用的時候,那個Activity可能已經不是正在顯示的那個Activity了,這個Fragment也可能已經不是正在顯示的那個Fragment了。

4、關於生命週期

通過add新增Fragment會觸發Fragment生命週期,hide和show不會觸發生命週期。

像微信那樣,一個Activity裡有四個Fragment,下面四個按鈕,點選一個按鈕顯示其中一個Fragment。這種情況下,為了優化效能,你可以這樣做:

起初只add第一個Fragment,其餘三個Fragment都不新增。點選某個標籤的時候先看對應的Fragment是否已經新增,沒有則new一個並add這個Fragment,隱藏其他所有Fragment;如果已經新增了就直接show這個Fragment,隱藏其他所有Fragment。

5、關於Fragment銷燬和重建

(1)Activity因記憶體不足、配置變化等原因被銷燬的時候,包含的Fragment也會被銷燬,無論此Fragment是否有id,系統都會呼叫Fragment的onSaveInstanceState,並且保留之前setArguments的Bundle,並且Activity的FragmentManager裡的Fragment會被記錄。

(2)由於(1)的原因,如果一個Activity裡的fragment是通過add方式新增的,那麼如果出現上述情況,就必須在Activity的add地方判斷是否應該重新新增Fragment,避免重複新增。

(3)剛剛在第1點時說到Fragment在Activity中的使用。那個地方說得還是有點問題。其實應該給每個Fragment設一個tag,在FragmentTransaction.add方法新增Fragment的時候,第三個引數使用這個tag。初始化的時候,應該先從FragmentManager裡檢視這個Fragment是否已經有,如果有就直接使用原來的,如果沒有再新建。

系統在重建Activity時會重建Fragment(呼叫Fragment的預設建構函式),並且將這些新增到FragmentManager裡,並且設定之前setArguments時使用的Bundle。此時系統回撥Fragment的onCreate(Bundle)、onActivityCreated(Bundle)、onViewStateRestored(Bundle)這類方法的時候,引數不為空。

注意:新建的Fragment和原來對應的Fragment不是同一個例項!

Activity內正確使用Fragment的程式碼如下:


FragmentManager fm = getSupportFragmentManager();
Fragment ft = fm.findFragmentByTag("MyFragment");
Bundle args = new Bundle();
args.putString("text","Hello");
MyFragment = ft == null ? MyFragment.newInstance(args) : (MyFragment)ft;
if (savedInstanceState==null){
	fm.beginTransaction()
		.add(R.id.fragment, ft, "MyFragment")
		.commitAllowingStateLoss();
}


相關文章