使用Fragment 官方例子中顯示:html
例如:一個學生Fragment,須要傳入studentId,進行http請求顯示,那麼setArguments後防止殺掉Fragment後,參數爲0,顯示不了數據。android
1 public static StudentFragment newInstance(int studentId){ 2 StudentFragment fragment = new StudentFragment(); 3 Bundle bundle = new Bundle(); 4 bundle.putInt("student_id", studentId); 5 fragment.setArguments(bundle); 6 return fragment; 7 }
setArguments:安全
1 private int mStudentId = 0; 2 3 @Override 4 public void setArguments(Bundle args) { 5 super.setArguments(args); 6 7 mStudentId = args.getInt("student_id", 0); 8 }
那麼app在殺死後,回到這個Fragment 時,會在onCreateView時,進行獲取參數,獲取正確的頁面數據,否則有可能會由於null,而崩毀。網絡
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mStudentId = getArguments().getInt("student_id", 0); return super.onCreateView(inflater, container, savedInstanceState); }
我爲何不主張使用Fragment數據結構
Fragment:( Fragment就至關於一個有生命週期的View,它的生命週期被所在的Activity的生命週期管理 )app
生命週期回調說明:異步
onAttach(Activity)
當Fragment與Activity發生關聯時調用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
建立該Fragment的視圖
onActivityCreated(Bundle)
當Activity的onCreate方法返回時調用
onDestoryView()
與onCreateView想對應,當該Fragment的視圖被移除時調用
onDetach()
與onAttach相對應,當Fragment與Activity關聯被取消時調用ide
與Activity 依賴關係:動畫
與ViewPager 的關係:ui
(1)setOffscreenPageLimit
1 // 預加載頁面的數量 2 viewPager.setOffscreenPageLimit(2);
知道ViewPager會有預加載的特性,我覺得Fragment_1會從 1 onAttach() 開始直到 6 onResume()後,Fragment_2纔會開始從 1onAttach()開始,到了3 onCreateView()會中止。然而,並非預想的這樣。
首先,Fragment_1 經歷 1_onAttach() 和 2_onCreate() 後, Fragment_2也開始走了 1_onAttach()和 2_onCreate()方法。
接着,Fragment_1依次經歷 3_onCreateView(), 4_onCreateActivity(),5 _onStart ,6_onResume()。此時,Fragment_1得到焦點,已經展現在手機屏幕。Fragment_2也接着從 3_onCreate()開始直到也執行到 6_onResume()方法。疑問就在這,我我的感受既然Fragment_2沒有在屏幕顯示,就不會執行到 6_onResume()方法,然而Log信息卻顯示執行到了 6_onResume()。這裏記錄一下。
多了Fragment_3的Log信息,並且走的方法和Frgment_1和2是同樣的。後面的狀況的Log信息便再也不貼出來了,本質是同樣的,只是多預加載了一個Fragment。但此時ViewPager依然只是保留3個Fragment的信息。當滑到Fragment_4的時候,Fragment_1走了7_onPause(),8_onStop(),9_onDestroyView()。Fragment_2,3,4則處於6_onResume()。
這個方法對Fragment生命週期方法的調用順序上並無什麼影響,只是預加載的Fragment的數量又設置的limit參數決定。
(2)setUserVisibleHint
setUserVisibleHint()這個方法會在預加載Fragment時,會在Fragment的1_onAttach()前調用。此時,在setUserVisibleHint()裏面調用也不用懼怕空指針的問題,由於有3個前條件。當經過滑動ViewPger時,根據6.1知道,每次滑動都會調用setUserVisibleHint()這個方法,進行預加載後,當滑到Fragment_2,3,4時,就會調用裏面的load()方法。
在Fragment的4_onCreateActivity()中調用load。我我的習慣把網絡請求放在這個生命週期。在Fragment_1和點擊Tab時,引發問題的緣由就是setUserVisibleHint()先於1_onAttach()調用,不能知足前提條件中的isCreate,因此load方法不會被調用。而4_onCreateActivity()中會再次調用load()方法,此時還知足3個前提條件。這樣,遺留的問題也解決了。ViewPager中Fragment延遲加載這個需求也能夠實現了。若是此時Fragment中有一個輪播圖的話,也能夠經過getUserVisibleHint()這個方法來選擇關閉輪播圖線程的時機。
Fragment Api:
Fragment經常使用的三個類:
android.app.Fragment 主要用於定義Fragment
android.app.FragmentManager 主要用於在Activity中操做Fragment
android.app.FragmentTransaction 保證一些列Fragment操做的原子性
主要的操做都是FragmentTransaction的方法:
1 // v4包中,getSupportFragmentManager 2 FragmentManager fm = getFragmentManager(); 3 // 開啓一個事務 (主要的操做都是FragmentTransaction的方法) 4 FragmentTransaction transaction = fm.benginTransatcion(); 5 6 // 往Activity中添加一個Fragment 7 transaction.add(Fragment fragment); 8 // 從Activity中移除一個Fragment,若是被移除的Fragment沒有添加到回退棧(回退棧後面會詳細說),這個Fragment實例將會被銷燬。 9 transaction.remove(Fragment fragment); 10 // 使用另外一個Fragment替換當前的,實際上就是remove()而後add()的合體 11 transaction.replace(R.id.XXX, Fragment fragment); 12 // 隱藏當前的Fragment,僅僅是設爲不可見,並不會銷燬 13 transaction.hide(Fragment fragment); 14 // 顯示以前隱藏的Fragment 15 transaction.show(Fragment fragment); 16 //提交一個事務 17 transatcion.commit();
// 會將view從UI中移除,和remove()不一樣,此時fragment的狀態依然由FragmentManager維護。
detach()
// 重建view視圖,附加到UI上並顯示。
attach();
判斷何時該使用什麼方法:
(1)但願保留用戶操做的面板,可使用hide和show。
(2)不但願保留用戶操做,可使用remove(),而後add();或者直接使用replace(),效果相同。
(3)remove會銷燬整個Fragment實例,而detach則只是銷燬其視圖結構,實例並不會被銷燬。
(4)當前Activity一直存在,那麼在不但願保留用戶操做的時候,能夠優先使用detach。
參考地址:Android Fragment 真正的徹底解析(上) Android Fragment 真正的徹底解析(下)
對於用法:
(1)getActivity() 方法空指針問題:
onAttach(Activity activity)
裏賦值,使用mActivity代替
getActivity()
,保證Fragment即便在
onDetach
後,仍持有Activity的引用(有引發內存泄露的風險,可是異步任務沒中止的狀況下,自己就可能已內存泄漏,相比Crash,這種作法「安全」些),即:
protected Activity mActivity; /** * API低於 23 的版本中不會去調用後者,只會去調用onAttach(Activity) */ @Override public void onAttach(Activity activity) { super.onAttach(activity); this.mActivity = activity; } /** * 若是用了support 23的庫,上面的方法會提示過期,且不會被調用,能夠用下面的方法代替 */ @Override public void onAttach(Context context) { super.onAttach(context); this.mActivity = (Activity) context; }
如不須要使用getActivity():
1 /* 2 * onAttach(Context) is not called on pre API 23 versions of Android and onAttach(Activity) is deprecated 3 * Use onAttachToContext instead 4 */ 5 @TargetApi(23) 6 @Override 7 public void onAttach(Context context) { 8 super.onAttach(context); 9 onAttachToContext(context); 10 } 11 12 /* 13 * Deprecated on API 23 14 * Use onAttachToContext instead 15 */ 16 @SuppressWarnings("deprecation") 17 @Override 18 public void onAttach(Activity activity) { 19 super.onAttach(activity); 20 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 21 onAttachToContext(activity); 22 } 23 } 24 25 /* 26 * Called when the fragment attaches to the context 27 */ 28 protected void onAttachToContext(Context context) { 29 //do something 30 }
(2)未必靠譜的出棧方法remove()
若是你想讓某一個Fragment出棧,使用remove()
在加入回退棧時並不靠譜。
若是你在add的同時將Fragment加入回退棧:addToBackStack(name)的狀況下,它並不能真正將Fragment從棧內移除,若是你在2秒後(確保Fragment事務已經完成)打印getSupportFragmentManager().getFragments()
,會發現該Fragment依然存在,而且依然能夠返回到被remove的Fragment,並且是空白頁面。
若是你沒有將Fragment加入回退棧,remove方法能夠正常出棧。
若是你加入了回退棧,popBackStack()
系列方法才能真正出棧,這也就引入下一個深坑,popBackStack(String tag,int flags)
等系列方法的BUG。
(3)Fragment轉場動畫
若是你的Fragment沒有轉場動畫,或者使用setCustomAnimations(enter, exit)
的話。
1 getFragmentManager().beginTransaction().setCustomAnimations(enter, exit); 3 // 若是經過tag/id同時出棧多個Fragment的狀況時, 4 // 請謹慎使用.setCustomAnimations(enter, exit, popEnter, popExit) 5 // 在support-25.4.0以前出棧多Fragment時,伴隨出棧動畫,會在某些狀況下發生異常 6 // 你須要搭配Fragment的onCreateAnimation()臨時取消出棧動畫,或者延遲一個動畫時間再執行一次上面提到的Hack方法,排序
若是想讓出棧動畫運做正常的話,須要使用Fragment的onCreateAnimation
中控制動畫:
1 @Override 2 public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 3 // 此處設置動畫 4 }
pop多個Fragment時轉場動畫 帶來的問題:
在使用 pop(tag/id)
出棧多個Fragment的這種狀況下,將轉場動畫臨時取消或者延遲一個動畫的時間再去執行其餘事務;
緣由在於這種情景下,可能會致使棧內順序錯亂(上文有提到),同時若是發生「內存重啓」後,由於Fragment轉場動畫沒結束時再執行其餘方法,會致使Fragment狀態不會被FragmentManager正常保存下來。
二、進入新的Fragment並馬上關閉當前Fragment 時的一些問題
(1)若是你想從當前Fragment進入一個新的Fragment,而且同時要關閉當前Fragment。因爲數據結構是棧,因此正確作法是先pop
,再add
,可是轉場動畫會有覆蓋的不正常現象,你須要特殊處理,否則會閃屏!
若是你遇到Fragment的mNextAnim空指針的異常(一般是在你的Fragment被重啓的狀況下),那麼你首先須要檢查是否操做的Fragment是否爲null;其次在你的Fragment轉場動畫還沒結束時,你是否就執行了其餘事務等方法;解決思路就是延遲一個動畫時間再執行事務,或者臨時將該Fragment設爲無動畫
對於一些操做,Fragment發生的生命週期變化:
切換到該Fragment:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume()
屏幕滅掉或者回到桌面(Home): onPause() -> onSaveInstanceState() -> onStop()
屏幕解鎖或者從新回到應用: onStart() -> onResume()
切換到其餘Fragment: onPause() -> onStop() -> onDestroyView()
切換回自己的Fragment: onCreateView() -> onActivityCreated() -> onStart() -> onResume()
退出應用:onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
參考: