ViewPager中切換界面Fragment被銷毀的問題分析
1、使用場景
ViewPager+Fragment實現界面切換,界面數量>=3


2、Fragment生命周期以及與Activity生命周期對比


3、問題描述
按上圖所說,只有當Fragment所Attached的Activity執行destroy的時候才會調用onDestoryView方法,然而現實是:
當界面由2切換到1的時候,3界面對應的Fragment實際上走了如下流程:
1 -->onPause 2 -->onStop 3 -->onDestroyView

再由1切換回2或者3時,3界面對應的Fragment的執行流程:
1 -->onCreateView 2 -->onStart 3 -->onResume
可見,界面3對應的Fragment被銷毀並重新創建。

4、原因分析
ViewPager的默認加載方式是緩存當前界面前后相鄰的兩個界面,即最多共緩存包括當前界面在內的三個界面信息。當滑動切換界面的時候,非相鄰界面信息將被釋放。
界面2是當前界面,界面1和3是緩存界面,當切換到1時,界面2仍緩存,界面3被銷毀釋放,於是便有了onDestroyView的調用。
由1切換到2或3時,界面3又被重新創建,於是走了onCreateView流程。
5、解決方案
- 方案一:設置ViewPager的緩存界面數
此方案適用於界面數較少的情況,避免緩存界面太多導致內存吃緊。
方法:
mPager .setOffscreenPageLimit(2);
參數:int limit - 緩存當前界面每一側的界面數
以上述為例,當前界面為1,limit = 2,表示緩存2、3兩個界面。如此便避免了界面3被銷毀。
- 方案二:保存狀態並恢復
此方案適用於可用界面信息可由狀態保存和恢復實現的情況。
在onDestroyView方法內保存相關信息,在onCreateView方法內恢復信息設置。
- 方案三(推薦):復用Fragment的RootView
此方案適用通用場景,推薦使用。
步驟1:在onDestroyView方法內把Fragment的RootView從ViewPager中remove
1 @Override 2 public void onDestroyView() { 3 LogUtils.d(TAG , "-->onDestroyView"); 4 super .onDestroyView(); 5 if (null != FragmentView) { 6 ((ViewGroup) mFragmentView.getParent()).removeView(mFragmentView); 7 } 8 }
步驟2:在onCreateView方法內復用RootView
1 @Override 2 public View onCreateView(LayoutInflater inflater, ViewGroup container, 3 Bundle savedInstanceState) { 4 LogUtils.d (TAG, "-->onCreateView"); 5 if (null == mFragmentView) { 6 mFragmentView = inflater.inflate(R.layout.fragment, container, false); 7 mListView = (ListView) mFragmentView .findViewById(R.id.mm_listview); 8 mListView.setAdapter(mAdapter); 9 mPbar = (ProgressBar) mFragmentView.findViewById(R.id.pbar_mm_loading); 10 mPbar.setVisibility(View.VISIBLE); 11 } 12 13 return mFragmentView ; 14 }