1. 程式人生 > >Android:Fragment在ViewPager中的生命週期

Android:Fragment在ViewPager中的生命週期

1.一直以來的疑問

Fragment在ViewPager到底經歷了哪些生命週期方法?到底發生了什麼?

常會TabLayout和ViewPager配合起來使用,針對這套組合,就想也做一些學習瞭解。在一個ViewPager中經常會存在多個Fragment,Fragemnt在ViewPager中的生命週期一直沒有鬧明白。這周正好在測試Api的時候又用到了TabLayout和ViewPager組合。ViewPager中的Fragment並想做到延遲載入,在可見的時候再進行網路請求。在敲程式碼的時候想到到幾個問題:
在ViewPager中,滑動時,Fragment會經歷哪些生命週期? ViewPager的setOffscreenPageLimit()方法對Fragment有哪些影響? 在ViewPager中,Fragment的setUserVisibleHint()對Fragment的生命週期有哪些影響? 點選TabLayout的Tab時,Fragment經歷的生命週期和滑動ViewPager有啥不一樣?

現在有個明確的需要:在TabLayout和ViewPager這個組合下,實現Fragment的延遲載入。

2.Tablayout、ViewPager組合

程式碼很簡單,就是新建一個Android Stuido工程,一個Activity裡面有一個TabLayout和ViewPager,ViewPager中四個Fragment。
這裡寫圖片描述
這裡寫圖片描述

2.1佈局檔案

Acivity的佈局檔案:


    </android.support.design.widget.tablayout></android.support.v7.widget.toolbar></android.support
.design.widget.appbarlayout> </android.support.v4.view.viewpager></android.support.design.widget.coordinatorlayout>

主要是CoordinatorLayout、AppBarLayout和Toolabr的使用。如果基礎的用法不知道的話可以看看CoordinatorLayout、Tablayout、Toolbar簡單組合使用,我寫的很基礎的用法。啊哈哈 :)

Fragment的佈局:

<framelayout android:layout_height
="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<textview android:gravity="center" android:layout_height="match_parent" android:layout_width="match_parent" android:text="1" android:textcolor="@color/colorPrimary" android:textsize="150sp"> </textview></android.support.v4.widget.nestedscrollview> </framelayout>

Fragmentde的佈局更加簡單,主要就一個TextView。4個Fragment佈局一樣,就貼出一個。

2.2Actiity程式碼

public class MainActivity extends AppCompatActivity {
    private TabLayout tabLayout;
    private ViewPager viewPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        initView();
    }

    private void initView() {
        tabLayout = (TabLayout) findViewById(R.id.tab_main_activity);
        viewPager = (ViewPager) findViewById(R.id.vp_main_activity);

        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);

        List <fragment> data = new ArrayList<>();
        data.add(new Fragment_1());
        data.add(new Fragment_2());
        data.add(new Fragment_3());
        data.add(new Fragment_4());

        adapter.setFragmentList(data);
        tabLayout.setupWithViewPager(viewPager);
        for (int i = 0 ; i < adapter.getCount() ; i ++){
             tabLayout.getTabAt(i).setText("Tab_"+(i+1));
        }
    }
}

主要就是initView()這個方法。主要就是把4個Fragment加入到ViewPager中。

2.3Fragment程式碼

Fragemnt的生命週期方法共有11個,為了下面能夠更加清晰記住這些生命週期,我給這些生命週期方法從1到11定了編號。如下:

1 onAttach()           --> 2 onCreate()      --> 3 onCreateView()    --> 4 onCreateActivity() 
--> 5 onStart()        --> 6 onResume()      --> 7 onPause()         --> 8 onStop() 
--> 9 onDestroyView()  --> 10 onDestroy()     --> 11 onDetach()

Fragment原本打算偷懶,只想寫一套,不想寫4個。但寫一個的話onAttach()不好做區分展示,就又老老實實寫了四個。

public class Fragment_1 extends Fragment {

    private final String TAG = "Fragment_1";


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        log("   1__onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        log("    2__onCreate");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        log("    3_onCreateView");
        return inflater.inflate(R.layout.fragment_layout_1,container,false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        log("   4__onActivityCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        log("   5__onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        log("   6__onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        log("   7__onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        log("   8__onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        log("   9__onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        log("   10__onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        log("   11__onDetach");
    }

    private void log (String methodName){
        Log.e(TAG,"-------->"+methodName);
    }
}

程式碼全部貼完。都很簡單。前面一個說了4個問題,到了思考第2,3個問題時,程式碼需要小改動。

3.開始分析問題 1

在ViewPager中,滑動時,Fragment會經歷哪些生命週期?

開始前,這裡沒有圖,就簡單交代一下,每個Fragment有一個對應的數字。TAB-1 對應Fragment-1,以此類推,4個TAB對應於4個Fragment。預設顯示Fragment1。

3.1 滑動ViewPager,切換Fragment

這部分所有的切換Fragment都是通過滑動ViewPager。不是點選Tab。在Activity中,ViewPager此時啥都沒有設定。

3.1.1 載入Fragment_1

當Activity中ViewPager載入完成時,手機螢幕顯示預設的Fragment1。此時,列印的Log資訊:
這裡寫圖片描述

此時列印的Log資訊與預想的有很大不同。知道ViewPager會有預載入的特性,我以為Fragment_1會從 1 onAttach() 開始直到 6 onResume()後,Fragment_2才會開始從 1onAttach()開始,到了3 onCreateView()會停止。然而,並不是預想的這樣。

首先,Fragment_1 經歷 1_onAttach() 和 2_onCreate() 後, Fragment_2也開始走了 1_onAttach()和 2_onCreate()方法。

3.1.2 由Fragment_1向右滑動到Fragment2_

滑動一下螢幕,ViewPager中由Fragment_1切換到Fragment_2,,螢幕顯示Fragment_2時,下面是Log資訊:
這裡寫圖片描述
這裡寫圖片描述
哎,我擦,咋就只有Fragment_3的資訊,Fragment_1和Fragment_2的呢?這Fragment_2沒有Log資訊可以理解,可Fragment_1應該會有啊。這裡又和預想的大不一樣。預想中Fragment_1會走 7__onPause(),8__onStop(),9__DestroyView()和10__ononDestroy()。而實際卻沒有走,開始還以為搞錯了,又反覆測試,發現就是隻有Fragment_3的Log資訊。

根據Log資訊,可以看出,當滑到Fragment_2後,Fragment_3經歷了從 1__onCreate()方法到6__onResume()方法。Fragment_1卻是沒有走任何生命週期方法。這裡考慮了一下,感覺也蠻合理。ViewPager展示的時候,使用者在實際使用的時候,經常會從一個Fragment滑到另一個Fragment後又切換回來。這時如果按照我預想的,Fragment_1中的View已經被銷燬,再次切換回來又需要重新繪製,這樣頻繁請求記憶體空間也不好。

這時,Fragment_3已經時刻準備好,如果在onCreateView()方法中有網路請求的話,網路請求在滑動到Fragment_2後就會被呼叫。就等著滑動ViewPager後,來展示內容。

這裡先預留問題滑到Fragment_2後,Fragment_1沒有走任何生命週期方法,Fragment_1的生命週期方法會在啥時候走?
這裡寫圖片描述
這裡寫圖片描述
當Fragment_2在螢幕時,這時候就有了兩個方向可以選擇,左滑到Fragment_1 或者 右滑到 Fragment_3。同理,當螢幕顯示Fragment_3的時候,也是有兩個方向可選擇。

3.1.3 由Fragment_2向右滑到Fragment_3

前面已經提到,當由Fragment_1滑到Frament_2後,Fragment_3的生命週期方法已經從 1_onCreate()走到了 6_onResume()。當滑到Fragment_3後,看下此時的Log資訊。
這裡寫圖片描述
這裡寫圖片描述
這次,Fragment_4先走了 1_onAttach(),2_onCreate()後,Fragment_1走 7_onPause,8_onStop,9_onDestroyView()。Fragment_1的生命週期終於開始走。而此時,Frment_4也已經完成了預載入。這裡也可以解答3.1.2最後預留的那個問題。

看到了這次的Log資訊,終於感覺看出了點ViewPager中巢狀Fragment後一些特點。

當ViewPager中的Fragment大於等於3個的時候,除去展示開頭和結尾兩個Fragment的情況,ViewPager會保留一個Fragment左右兩側以及自身3個Fragment的資訊。例如,當滑到Fragment_2的時候,Fragment_3也已經走到了6_onResume()這個生命週期方法,而此時,Fragemnt_1沒有走任何的生命週期方法,還在ViewPager中保留著。此外還有,當載入本身或者預載入下一個Frgment時,只是先走1_onAttach()和2_onCreate()兩個生命週期方法後,才會根據當前情況確定繼續走相應的Fragment的生命週期方法。

3.1.4 由Fragment_2向左滑到Fragment_1

當由Frament_1滑到Fragment_2時,並沒有走Fragment_1和Fragment_2的生命週期,而是走了Fragment從1_onCreate()到6_onResume()。接下來,螢幕向左滑,由Fragment_2滑到Fragment_1。Log資訊:
這裡寫圖片描述
這裡寫圖片描述
由Fragment_1滑到Fragment_2只是走了Fragment_3的生命週期方法,而由Fragment_2滑到Fragment_1時,也是隻走了Fragment的生命週期方法。在3.1.2中,當滑到Fragment_2後,Fragment_3已經走到了6_onResume()方法。再滑到Fragment_1後,Fragment_3走了7_onPause(),8_onStop(),9_onDestoryView()。到了這裡發現,在ViewPager中,相鄰的3個Fragment之間來回切換,都沒有走10_onDestroy()和11_onDetach()。

到了這裡,四個Fragment滑動的情況再分析檢視下面的3種情況。其實,下面的這些情況和前面的情況重複了,本質是一樣的。
由Fragment_3向右滑到Fragment_4 由Fragment_3向左滑到Fragment_2 由Fragment_4向左滑到Fragment_3

3.1.5 由Fragment_3向右滑到Fragment_4

滑動前,當螢幕顯示Fragment_3時,此時,由3.1.3知道,Fragment_1走到了9_onDestroyiew()。Fragment_4走到了6_onResume()。
滑到Fragment_4後:
這裡寫圖片描述
這裡寫圖片描述
Fragment_4是ViewPager中最後一個Fragment所以也就沒了下一個Fragment預載入。只是不相鄰的Fragment_2走了7_pause(),8_onStop,9_onDestroyView()方法。此時,就可以結合3.1.3情況來看。實際的情況是一樣的,只是區別在於Fragment_4已經是最後一個Fragment了。

3.1.6 由Fragment_3向左滑到Fragment_2

這裡結合3.1.4,很容易就理解了。
滑動前,當螢幕顯示Fragment_3時,此時,由3.1.3知道,Fragment_1走到了9_onDestroyiew()。Fragment_4走到了6_onResume()。
滑動到Fragment_2後:
這裡寫圖片描述
這裡寫圖片描述
根據3.1.3中的Log資訊,Frgment_1在由Fragment_2滑到Fragment_3的時候,生命週期已經走到了9_onDestroyView(),並沒有走到10_onDestroy()。當由Fragment_3滑到Fragment_2後,ViewPager再次預載入Fragment_1時,是從3_onCreateView()開始的,走到4_onCreateAcitivty()後,開始走Fragment_4的生命週期方法7_onPause(),8_onStop(),9_onDestroyView(),之後,Fragment_1的生命週期走4_onCreateActivity(),5_onStart(),6_onResume()。

3.1.7 由Fragment_4向左滑到Fragment_3

滑動前,當顯示Fragment_4時,根據3.1.5的Log資訊,Fragment_2走到了9_onDestroyView()生命週期方法。而Fragment_3和Fragment_4此時都處於6_onResume()這個生命週期方法。
滑動到Fragment_3後:
這裡寫圖片描述
這裡寫圖片描述
到了這裡,就比較容易理解此時的Log資訊。Fragment_2從3_onCreateView()走到了6_onResume()方法。到了此時,Fragment_2、Fragment_3和Fragment_4此時3個Fragment都處於6_onResume()這個生命週期方法。

到了這裡,ViewPager中Fragment滑動的情況就差不多分析完了。其他都是些重複的情況了。根據3.1.1到3.1.7的Log來看,ViewPager中巢狀Fragment預設不設定其他方法時,若Fragment的數量大於等於3時,ViewPager會保留包括一個Fragment在內左右兩側3個Fragment的資訊。然而這裡也留下了個問題,Fragment的10_onDestroy和11_onDetach()什麼時候會走?

3.1.8 點選回退鍵finish掉Acitivty時

根據3.1.7的資訊,此時,Fragment_1處於9_onDestroyView()這個生命週期方法,Fragment_2、Fragment_3和Fragment_4此時3個Fragment都處於6_onResume()這個生命週期方法。
此時,點選back鍵結束當前的Acitivty,我這裡整個測試的App就一個Activity,點選了back鍵就會退出應用。看下此時的Log資訊:
這裡寫圖片描述
這裡寫圖片描述
根據Log資訊,Fragment_2,3,4先走了7_onPause()後,走了8_onStop()。接著,便是Fragment_1走10_onDestroy(),11_onDetach()。之後便是Fragment_2,3,4依次走9_onDestroyView(),10_onDestroy(),11_onDetach()。

到了這裡,3.1.7的問題也就有了答案。ViewPager中,Fragment的10_onDestroy()以及11_onDetach()會在ViewPager所在的Activity結束後被呼叫。

這裡還有一點需要說的是,點選back鍵後,圖(點選Back鍵時)中的Log資訊並不是一次打印出來的。一開始我以為是我是看錯了,又測試了幾次,發現,Fragment_2,3,4走了7_onPause()這個方法後,確實會短暫停一下,非常快的又打印出下面的Log資訊。

3.1.9 當螢幕顯示Fragment_3時,點選電源鍵關閉螢幕

當螢幕正在展示某個Fragment時,點選了電源鍵或者手機自動息屏時,這種情況下,會和以上的幾種情況有所不同,下面還是以Fragment_3為例來嘗試分析一下。

根據3.1.7的資訊,此時,Fragment_1處於9_onDestroyView()這個生命週期方法,Fragment_2、Fragment_3和Fragment_4此時3個Fragment都處於6_onResume()這個生命週期方法。

點選電源鍵之後:
這裡寫圖片描述
Fragment_2,3,4依次走了7_onPause(),8_onStop()。接下來,再點亮螢幕,顯示Fragment_3:
這裡寫圖片描述
Fragment_2,3,4依次走了5_onStart(),6_onResume()。

到了此時,ViewPager中Fragment滑動的情況就結束了。這些情況,寫一個很簡單的測試demo就可以搞的比較清楚了。要一次就記得清楚也不算特別現實,多想幾次就可以了。

4.嘗試分析問題2

ViewPager的setOffscreenPageLimit()方法對Fragment有哪些影響?

直白翻譯就是設定幕後頁面限制 :) 。其實就是設定預載入Fragment的數量。

public void setOffscreenPageLimit(int limit) {
       if (limit < DEFAULT_OFFSCREEN_PAGES) {
           Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                   DEFAULT_OFFSCREEN_PAGES);
           limit = DEFAULT_OFFSCREEN_PAGES;
       }
       if (limit != mOffscreenPageLimit) {
           mOffscreenPageLimit = limit;
           populate();
       }
   }

通過ViewPager中原始碼可以看出,傳入的引數如果小於DEFAULT_OFFSCREEN_PAGES(就是1)這個值是無效的。所以嘗試使用setOffscreenPageLimit(0)來關閉ViewPager的預載入是無效。也就是說,limit只有大於等於2時才會有效。Ps:ViewPager呼叫這個方法後,裡面populate()這個方法又做了啥,我目前想關心也關心不了,大概看了下,ViewPager共有3000多行程式碼,我目前的程式碼閱讀能力還很低,即使想做到只是閱讀相關程式碼也困難,目前想讀通比較困難,以後程式碼閱讀能力提升再來看了。

接下來,改動程式碼,在ViewPager中,加入viewPager.setOffscreenPageLimit(2)這行程式碼,再次執行,看看Log資訊。
這裡寫圖片描述

根據問題1的分析,看到這個Log資訊就很好理解了,多了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引數決定。

5.嘗試分析問題3

在ViewPager中,Fragment的setUserVisibleHint()對Fragment的生命週期有哪些影響?

再次顧名思義:設定使用者可見暗示 。:) 這個方法可以用來判斷Fragment是否可見。這裡也不再貼原始碼了。

5.1 Fragment加入setUserVisibleHint()

修改程式碼前,我把在分析問題2時加入的viewPager.setOffscreenPageLimit(2)註釋掉。這樣Log資訊會少一些。在每個Fragment中程式碼做了一些修改加入了一個setUserVisibleHint()。

@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        log("setUserVisibleHint");
}

再次執行Demo,檢視Log資訊:
這裡寫圖片描述

和預想的大不一樣。setUserVisibleHint()竟然最先被呼叫。預想的是1_onAttach()執行後,才會走setUserVisibleHint()這個方法。而且,Fragment_1走了兩次setUserVisibleHint()這個方法。其他的Log資訊倒是比較熟悉了。
滑動螢幕,滑動到Fragment_2,看下此時的Log資訊:
這裡寫圖片描述
這時不僅先執行了Fragment_3的setUserVisibleHint(),連Fragment_1,2的setUserVisibleHint()方法也比Fragment_3的1_onAttach()。此時,Fragment_1,2,3都處於6_onResume()。再由Fragment_2滑到Fragment_1,看下Log:
這裡寫圖片描述
此時,Fragment_1,2的setUserVisibleHint()方法優先被呼叫,Fragment_3走到9_onDestroyView()方法。

到了這裡,感覺setUserVisibleHint()這個方法每次滑動都會被呼叫,而且最先被呼叫,ViewPager中儲存資訊的Fragment都會被呼叫。

接下來模擬一下延遲載入網路請求。

5.2 延遲載入,模擬網路請求

在每個Fragment中,加入一個Boolean值,並將程式碼做一些改動。

@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        isCreate = true;
        log("   2__onCreate");
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isCreate && isVisibleToUser){
            log("開始進行網路請求了");
            isCreate = false;
        }
    }

當Fragment可見時,進行網路請求。網路請求加上了前提條件。除了isVisibleToUser為true外還有一個isCreate。Fragment走了2_onCreate()iSCreate設為了true。我通常會用Fragment.newInstrance()這個方法進行初始化Fragment時來傳值,在2_onCreate()方法中通過getArguments()來接收值,所以這裡加了一個條件isCreate為true。

再次執行Demo,看Log資訊:
這裡寫圖片描述
我擦,我的”開始進行網路請求了”呢?最關鍵的Log資訊卻沒有看到。根據5.1,每次肯定會呼叫setUserVisibleHint()這個方法的啊,可Fragment_1明明都已經可見了,怎麼沒有出現”開始進行網路請求了”?於是,看了下原始碼,…,白看,沒瞧出啥。好吧,我再滑下螢幕,Fragment_2顯示,看下Log:
這裡寫圖片描述
滑到Fragment_2後,第一行倒是打印出來了”開始進行網路請求了”。繼續滑,Fragment_3,4也都打印出來了。然後我又依次從Fragment_4滑到了Fragment_1後,Log資訊也打出了”E/Fragment_1: ——–>開始進行網路請求了”。

可為啥Fragment_1第一次顯示時,沒有列印呢?
這裡結合5.1想了一下,setUserVisibleHint()這個方法在1_onAttach()方法之前。Fragment_1第一次載入時,2_onCreate()方法還沒走呢,此時isCreate的值還是fasle,就不會列印”開始進行網路請求了”。根據6.1的Log資訊,Fragment_1兩次呼叫setUserVisibleHint()時,isCreate都為false。而isCreate值變為true後,卻不在執行setUserVisibleHint()方法了。

考慮到了原因了,把判斷條件isCreate去掉也不是很合適。因為去掉了,此時也拿不到Fragment.newInstrance()傳遞過來的物件,往往網路請求的需要的引數需要這個傳遞過來的物件,會造成空指標。那怎麼辦?嗯,這是Fragment_1,第一個Fragment幹嘛還要延遲載入啊。延遲載入的目的就是為了等到Fragment_2,3,4…這些後面的Fragment可見後再進行網路請求。Fragment_1沒有太多必要載入。如果是考慮到使用者從Fragment_4向左滑,滑到Fragment_2時,Fragment_1不會再次進行網路請求的話,可以考慮使用其他的方法。例如,設定下拉重新整理,只有下拉重新整理Fragment_1才再次進行網路請求。

於是,關於Fragment_1,我的處理是,不使用延遲載入。

6.嘗試分析問題4

點選TabLayout的Tab時,Fragment經歷的生命週期和滑動ViewPager有啥不一樣?

問題1,2,3都是通過滑動來分析的,沒有通過點選Tab。現在來看看點選Tab。

6.1 點選Tab時,Fragment所走的生命週期

開始前,把每個Fragment中的setUserVisibleHint()先註釋掉,之後執行Demo。這裡點選Tab_2是體現不出啥效果的,因為Fragment_2已經預載入了。我這裡點選Tab_3,看Log資訊:
這裡寫圖片描述
到了這裡,這個Log資訊還是比較容易理解了。此時的Fragment_2,3,4處於6_onResume()這個方法。Fragment_1走到9_onDestroyView()。和滑動時其實沒有本質的區別。就是點選可以從Fragment_1越過Fragment_2而直接到Fragment_3這種互動上的區別而造成的少了滑動時對Fragment_3的預載入。如果是從Fragment_1滑到Fragment_3,在滑到Fragment_2時,就已經完成了對Fragment_3的預載入。而通過點選的方式時,點選Tab_3後,Fragment_3是從1_onAttach()這個生命週期方法開始的。

6.2 點選Tab的方式和Fragment延遲載入遇到的問題

在5.2中遇到的一個問題就是,Fragment_1用了我寫的那種延遲載入的方法時,第一次載入不會進行網路請求,只有再次滑到Fragment_1後才會進行網路請求。到了這裡,同樣也會引起這個問題。少了預載入,例如6.1中,直接點選Tab_3時,此時Fragment_3並沒有預載入。setUesrVisibleHint()的方法又比1_onAttach()方法呼叫的早。會有和6.2中遇到的問題一樣。第一次點選Tab_3後,並不會進行網路請求,只有再次滑動到Fragment_3後才會進行網路請求。

我的解決辦法就是利用ViewPager.setoffscreenLimit(int limit);這個方法。根據情況,我將litmit設為了3。我這個方法比較粗暴,雖然有效但不好,而且侷限性很大。一旦Tab可以編輯,並且數量不是固定的時候,這個方法就不好用了。而且預載入的數量多,肯定會需要更多的記憶體。這裡,也希望有哪位大神有好的解決辦法可以告訴我。

7 總結

這個週末用了一天半,寫好了這篇部落格,用來記錄下我的學習過程。寫的內容也是比較淺顯,水平太菜,如果能有能力閱讀原始碼,對於問題2,3,4會有更深入的瞭解,會有好的辦法,而不是這篇部落格中折中的方法。如果有啥錯誤,請趕緊指出。:)

8 補充

這篇部落格剛剛發出去,就看到了另外一篇部落格,專門講的懶載入。看了下,發現我遺留的問題很容易就可以解決。
修改每個Fragment的程式碼如下:

@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        isCreate = true;
        log("   2__onCreate");
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        load();
    }

    private void load() {
        if (isCreate && getUserVisibleHint() && !isHasLaodOnce){
            log("開始進行網路請求了");
            isCreate = false;
            isHasLaodOnce = true;
        }
    }

     @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        log("   4__onActivityCreated");
        load();
    }

load()方法有3個前提條件,isCreate,可見,沒有進行網路請求過。

根據5.1,setUserVisibleHint()這個方法會在預載入Fragment時,會在Fragment的1_onAttach()前呼叫。此時,在setUserVisibleHint()裡面呼叫也不用害怕空指標的問題,因為有3個前條件。當通過滑動ViewPger時,根據6.1知道,每次滑動都會呼叫setUserVisibleHint()這個方法,進行預載入後,當滑到Fragment_2,3,4時,就會呼叫裡面的load()方法。

針對5.2和6.2的問題。解決辦法就是在Fragment的4_onCreateActivity()中呼叫load。我個人習慣把網路請求放在這個生命週期。在Fragment_1和點選Tab時,引起問題的原因就是setUserVisibleHint()先於1_onAttach()呼叫,不能滿足前提條件中的isCreate,所以load方法不會被呼叫。而4_onCreateActivity()中會再次呼叫load()方法,此時還滿足3個前提條件。這樣,遺留的問題也解決了。ViewPager中Fragment延遲載入這個需求也可以實現了。如果此時Fragment中有一個輪播圖的話,也可以通過getUserVisibleHint()這個方法來選擇關閉輪播圖執行緒的時機。