2017年05月30
最后的懶加載寫的不好雅采,推薦請(qǐng)叫我大蘇
同學(xué)寫的Fragment
懶加載博客痕囱,
【Android】再來一篇Fragment的懶加載(只加載一次哦)
在大蘇同學(xué)的博客評(píng)論里,看到了另一個(gè)實(shí)現(xiàn)思路纬傲,有提到可以使用okHttp
的緩存機(jī)制棍掐,有空也再學(xué)一下
1. 一直以來的疑問<p>
Fragment在ViewPager到底經(jīng)歷了哪些生命周期方法楔壤?到底發(fā)生了什么?
常會(huì)TabLayout和ViewPager配合起來使用肌稻,針對(duì)這套組合清蚀,就想也做一些學(xué)習(xí)了解。在一個(gè)ViewPager中經(jīng)常會(huì)存在多個(gè)Fragment爹谭,F(xiàn)ragemnt在ViewPager中的生命周期一直沒有鬧明白枷邪。這周正好在做一個(gè)測(cè)試的時(shí)候又用到了TabLayout和ViewPager組合。ViewPager中的Fragment并想做到延遲加載诺凡,在Fragment可見的時(shí)候再進(jìn)行網(wǎng)絡(luò)請(qǐng)求东揣。在敲代碼的時(shí)候想到到幾個(gè)問題:
1. 在ViewPager中,滑動(dòng)時(shí)腹泌,F(xiàn)ragment會(huì)經(jīng)歷哪些生命周期嘶卧?
2. ViewPager的setOffscreenPageLimit()方法對(duì)Fragment有哪些影響?
3. Fragment的setUserVisibleHint()對(duì)Fragment的生命周期有哪些影響凉袱?
4. 點(diǎn)擊TabLayout的Tab時(shí)芥吟,F(xiàn)ragment經(jīng)歷的生命周期和滑動(dòng)ViewPager有啥不一樣侦铜?
現(xiàn)在有個(gè)明確的需求:在TabLayout和ViewPager這個(gè)組合下,實(shí)現(xiàn)Fragment的延遲加載钟鸵。
2. Tablayout钉稍、ViewPager組合 <p>
代碼很簡(jiǎn)單,就是新建一個(gè)Android Stuido工程棺耍,一個(gè)Activity里面有一個(gè)TabLayout和ViewPager贡未,ViewPager中四個(gè)Fragment。
2.1 布局文件 <p>
Activity的布局文件:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.fragmentlifeinviewpager.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:title="英勇青銅5"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<android.support.design.widget.TabLayout
android:id="@+id/tab_main_activity"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:tabMode="fixed"
app:tabSelectedTextColor="@color/colorAccent"
app:tabTextColor="@android:color/white"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/vp_main_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout>
主要是CoordinatorLayout烈掠、AppBarLayout和Toolabr的使用羞秤。如果基礎(chǔ)的用法不知道的話可以看看CoordinatorLayout、Tablayout左敌、Toolbar簡(jiǎn)單組合使用,我寫的很基礎(chǔ)的用法瘾蛋。啊哈哈 :)
Fragment的布局:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/colorPrimary"
android:textSize="150sp"
android:text="1"
android:gravity="center"
/>
</android.support.v4.widget.NestedScrollView>
</FrameLayout>
Fragmentde的布局更加簡(jiǎn)單,主要就一個(gè)TextView矫限。4個(gè)Fragment布局一樣哺哼,就貼出一個(gè)。
2.2 Actiity代碼 <p>
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()這個(gè)方法叼风。主要就是把4個(gè)Fragment加入到ViewPager中取董。
2.3 Fragment代碼 <p>
Fragemnt的生命周期方法共有11個(gè),為了下面能夠更加清晰記住這些生命周期无宿,我給這些生命周期方法從1到11定了編號(hào)茵汰。如下:
1_onAttach() --> 2_onCreate() --> 3_onCreateView() --> 4_onCreateActivity()
--> 5_onStart() --> 6_ onResume() --> 7_onPause() --> 8_onStop()
--> 9_onDestroyView() --> 10_onDestroy() --> 11_onDetach()
Fragment原本打算偷懶,只想寫一套孽鸡,不想寫4個(gè)蹂午。但寫一個(gè)的話onAttach()不好做區(qū)分展示,就又老老實(shí)實(shí)寫了四個(gè)彬碱。
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);
}
}
代碼全部貼完豆胸。都很簡(jiǎn)單。前面一個(gè)說了4個(gè)問題巷疼,到了思考第2,3個(gè)問題時(shí)晚胡,代碼需要小改動(dòng)。
3. 開始分析問題 1 <p>
- 在ViewPager中嚼沿,滑動(dòng)時(shí)估盘,F(xiàn)ragment會(huì)經(jīng)歷哪些生命周期?
開始前骡尽,這里沒有圖忿檩,就簡(jiǎn)單交代一下,每個(gè)Fragment有一個(gè)對(duì)應(yīng)的數(shù)字爆阶。TAB-1 對(duì)應(yīng)Fragment-1燥透,以此類推沙咏,4個(gè)TAB對(duì)應(yīng)于4個(gè)Fragment。默認(rèn)顯示Fragment1班套。
3.1 滑動(dòng)ViewPager肢藐,切換Fragment <p>
這部分所有的切換Fragment都是通過滑動(dòng)ViewPager。不是點(diǎn)擊Tab吱韭。在Activity中吆豹,ViewPager此時(shí)啥都沒有設(shè)置。
3.1.1 加載Fragment_1 <p>
當(dāng)Activity中ViewPager加載完成時(shí)理盆,手機(jī)屏幕顯示默認(rèn)的Fragment1痘煤。此時(shí),打印的Log信息:
此時(shí)打印的Log信息與預(yù)想的有很大不同猿规。我以為Fragment_1會(huì)從 1 onAttach() 開始直到 6 onResume()后衷快,F(xiàn)ragment_2才會(huì)開始從 1onAttach()開始,到了3 onCreateView()會(huì)停止姨俩。然而蘸拔,并不是預(yù)想的這樣。
首先环葵,F(xiàn)ragment_1 經(jīng)歷 1_onAttach() 和 2_onCreate() 后调窍, Fragment_2也開始走了 1_onAttach()和 2_onCreate()方法。
接著张遭,F(xiàn)ragment_1依次經(jīng)歷 3_onCreateView(), 4_onCreateActivity(),5 _onStart ,6_onResume()邓萨。此時(shí),F(xiàn)ragment_1獲得焦點(diǎn)菊卷,已經(jīng)展示在手機(jī)屏幕缔恳。Fragment_2也接著從 3_onCreate()開始直到也執(zhí)行到 6_onResume()方法。疑問就在這的烁,我個(gè)人感覺既然Fragment_2沒有在屏幕顯示褐耳,就不會(huì)執(zhí)行到 6_onResume()方法,然而Log信息卻顯示執(zhí)行到了 6_onResume()诈闺。這里記錄一下渴庆。
3.1.2 Fragment_1向右滑動(dòng)到Fragment_2 <p>
滑動(dòng)一下屏幕,ViewPager中由Fragment_1切換到Fragment_2雅镊,屏幕顯示Fragment_2時(shí)襟雷,下面是Log信息:
哎,我擦仁烹,咋就只有Fragment_3的信息耸弄,F(xiàn)ragment_1和Fragment_2的呢?這Fragment_2沒有Log信息可以理解卓缰,可Fragment_1應(yīng)該會(huì)有啊计呈。這里又和預(yù)想的大不一樣砰诵。預(yù)想中Fragment_1會(huì)走 7__onPause(),8__onStop(),9__onDestroyView()和10__onDestroy()。而實(shí)際卻沒有走捌显,開始還以為搞錯(cuò)了茁彭,又反復(fù)測(cè)試,發(fā)現(xiàn)就是只有Fragment_3的Log信息扶歪。
根據(jù)Log信息理肺,可以看出,當(dāng)滑到Fragment_2后善镰,F(xiàn)ragment_3經(jīng)歷了從 1__onCreate()方法到6__onResume()方法妹萨。Fragment_1卻是沒有走任何生命周期方法。這里考慮了一下炫欺,感覺也蠻合理乎完。ViewPager展示的時(shí)候,用戶在實(shí)際使用的時(shí)候竣稽,經(jīng)常會(huì)從一個(gè)Fragment滑到另一個(gè)Fragment后又切換回來囱怕。這時(shí)如果按照我預(yù)想的,F(xiàn)ragment_1中的View已經(jīng)被銷毀毫别,再次切換回來又需要重新繪制娃弓,這樣頻繁請(qǐng)求內(nèi)存空間也不好。
這時(shí)岛宦,F(xiàn)ragment_3已經(jīng)時(shí)刻準(zhǔn)備好台丛,如果在onCreateView()方法中有網(wǎng)絡(luò)請(qǐng)求的話,網(wǎng)絡(luò)請(qǐng)求在滑動(dòng)到Fragment_2后就會(huì)被調(diào)用砾肺。就等著滑動(dòng)ViewPager后挽霉,來展示內(nèi)容。
這里先預(yù)留問題:為什么滑到Fragment_2后变汪,F(xiàn)ragment_1沒有走任何生命周期方法侠坎?Fragment_1的生命周期方法會(huì)在啥時(shí)候走?
當(dāng)Fragment_2在屏幕時(shí)实胸,這時(shí)候就有了兩個(gè)方向可以選擇,左滑到Fragment_1 或者 右滑到 Fragment_3番官。同理庐完,當(dāng)屏幕顯示Fragment_3的時(shí)候,也是有兩個(gè)方向可選擇徘熔。
3.1.3 由Fragment_2向右滑到Fragment_3 <p>
前面已經(jīng)提到门躯,當(dāng)由Fragment_1滑到Frament_2后,F(xiàn)ragment_3的生命周期方法已經(jīng)從 1_onCreate()走到了 6_onResume()酷师。當(dāng)滑到Fragment_3后讶凉,看下此時(shí)的Log信息染乌。
這次,F(xiàn)ragment_4先走了1_onAttach(),2_onCreate()后懂讯,F(xiàn)ragment_1走 7_onPause,8_onStop,9_onDestoryView()慕匠。Fragment_1的生命周期終于開始走。這里也可以解答3.1.2最后預(yù)留的那個(gè)問題。
看到了這次的Log信息,終于感覺看出了點(diǎn)ViewPager中嵌套Fragment后一些特點(diǎn)粗仓。
當(dāng)ViewPager中的Fragment大于等于3個(gè)的時(shí)候,除去展示開頭和結(jié)尾兩個(gè)Fragment的情況锅铅,ViewPager會(huì)保留一個(gè)Fragment左右兩側(cè)以及自身3個(gè)Fragment的信息。例如减宣,當(dāng)滑到Fragment_2的時(shí)候盐须,F(xiàn)ragment_3也已經(jīng)走到了6_onResume()這個(gè)生命周期方法,而此時(shí)漆腌,F(xiàn)ragemnt_1沒有走任何的生命周期方法贼邓,還在ViewPager中保留著。此外還有闷尿,當(dāng)加載本身或者預(yù)加載下一個(gè)Frgment時(shí)塑径,只是先走1_onAttach()和2_onCreate()兩個(gè)生命周期方法后,才會(huì)根據(jù)當(dāng)前情況確定繼續(xù)走相應(yīng)的Fragment的生命周期方法填具。
3.1.4 由Fragment_2向左滑到Fragment_1 <p>
當(dāng)由Frament_1滑到Fragment_2時(shí)统舀,并沒有走Fragment_1和Fragment_2的生命周期,而是走了Fragment從1_onCreate()到6_onResume()劳景。接下來誉简,屏幕向左滑,由Fragment_2滑到Fragment_1盟广。
Log信息:
由Fragment_1滑到Fragment_2只是走了Fragment_3的生命周期方法闷串,而由Fragment_2滑到Fragment_1時(shí),也是只走了Fragment的生命周期方法筋量。在3.1.2中烹吵,當(dāng)滑到Fragment_2后,F(xiàn)ragment_3已經(jīng)走到了6_onResume()方法毛甲。再滑到Fragment_1后年叮,F(xiàn)ragment_3走了7_onPause(),8_onStop(),9_onDestoryView()具被。到了這里發(fā)現(xiàn)玻募,在ViewPager中,相鄰的3個(gè)Fragment之間來回切換一姿,都沒有走10_onDestroy()和11_onDetach()七咧。
到了這里跃惫,四個(gè)Fragment滑動(dòng)的情況再分析查看下面的3種情況。其實(shí)艾栋,下面的這些情況和前面的情況重復(fù)了爆存,本質(zhì)是一樣的。
- 由Fragment_3向右滑到Fragment_4
- 由Fragment_3向左滑到Fragment_3
- 由Fragment_4向左滑到Fragment_3
3.1.5 由Fragment_3向右滑到Fragment_4 <p>
滑動(dòng)前蝗砾,當(dāng)屏幕顯示Fragment_3時(shí)先较,此時(shí),由3.1.3知道悼粮,F(xiàn)ragment_1走到了9_onDestroyiew()闲勺。Fragment_4走到了6_onResume()。
滑到Fragment_4后:
Fragment_4是ViewPager中最后一個(gè)Fragment所以也就沒了下一個(gè)Fragment預(yù)加載扣猫。只是不相鄰的Fragment_2走了7_pause(),8_onStop,9_onDestroyView()方法菜循。此時(shí),就可以結(jié)合3.1.3情況來看申尤。實(shí)際的情況是一樣的癌幕,只是區(qū)別在于Fragment_4已經(jīng)是最后一個(gè)Fragment了。
3.1.6 由Fragment_3向左滑到Fragment_2 <p>
這里結(jié)合3.1.4昧穿,很容易就理解了勺远。
滑動(dòng)前,當(dāng)屏幕顯示Fragment_3時(shí)时鸵,此時(shí)谚中,由3.1.3知道,F(xiàn)ragment_1走到了9_onDestroyiew()寥枝。Fragment_4走到了6_onResume()宪塔。
滑動(dòng)到Fragment_2后:
根據(jù)3.1.3中的Log信息,F(xiàn)rgment_1在由Fragment_2滑到Fragment_3的時(shí)候囊拜,生命周期已經(jīng)走到了9_onDestroyView()某筐,并沒有走到10_onDestroy()。當(dāng)由Fragment_3滑到Fragment_2后冠跷,ViewPager再次預(yù)加載Fragment_1時(shí)南誊,是從3_onCreateView()開始的,走到4_onCreateAcitivty()后蜜托,開始走Fragment_4的生命周期方法抄囚。
3.1.7 由Fragment_4向左滑到Fragment_3 <p>
滑動(dòng)前,當(dāng)顯示Fragment_4時(shí)橄务,根據(jù)3.1.5的Log信息幔托,F(xiàn)ragment_2走到了9_onDestroyView()生命周期方法。而Fragment_4此時(shí)處于6_onResume()這個(gè)生命周期方法。
滑動(dòng)到Fragment_3后:
到了這里重挑,就比較容易理解此時(shí)的Log信息嗓化。Fragment_2從3_onCreateView()走到了6_onResume()方法。到了此時(shí)谬哀,F(xiàn)ragment_2刺覆、Fragment_3和Fragment_4此時(shí)3個(gè)Fragment都處于6_onResume()這個(gè)生命周期方法。
到了這里史煎,ViewPager中Fragment滑動(dòng)的情況就差不多分析完了谦屑。其他都是些重復(fù)的情況了。根據(jù)3.1.1到3.1.7的Log來看篇梭,ViewPager中嵌套Fragment默認(rèn)不設(shè)置其他方法時(shí)伦仍,若Fragment的數(shù)量大于等于3時(shí),ViewPager會(huì)保留包括一個(gè)Fragment在內(nèi)左右兩側(cè)3個(gè)Fragment的信息很洋。然而這里也留下了個(gè)問題充蓝,Fragment的10_onDestroy和11_onDetach()什么時(shí)候會(huì)走?
3.1.8 點(diǎn)擊回退鍵finish掉Acitivty時(shí) <p>
根據(jù)3.1.7的信息喉磁,此時(shí)谓苟,F(xiàn)ragment_1處于9_onDestroyView()這個(gè)生命周期方法,F(xiàn)ragment_2协怒、Fragment_3和Fragment_4此時(shí)3個(gè)Fragment都處于6_onResume()這個(gè)生命周期方法涝焙。
此時(shí),點(diǎn)擊back鍵結(jié)束當(dāng)前的Acitivty孕暇,我這里整個(gè)測(cè)試的App就一個(gè)Activity仑撞,點(diǎn)擊了back鍵就會(huì)退出應(yīng)用⊙希看下此時(shí)的Log信息:
根據(jù)Log信息隧哮,F(xiàn)ragment_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中承二,F(xiàn)ragment的10_onDestroy()以及11_onDetach()會(huì)在ViewPager所在的Activity結(jié)束后被調(diào)用榆鼠。
這里還有一點(diǎn)需要說的是,點(diǎn)擊back鍵后亥鸠,圖(點(diǎn)擊Back鍵時(shí))中的Log信息并不是一次打印出來的妆够。一開始我以為是我是看錯(cuò)了,又測(cè)試了幾次,發(fā)現(xiàn)责静,F(xiàn)ragment_2,3,4走了7_onPause()這個(gè)方法后,確實(shí)會(huì)短暫停一下盖桥,非吃煮Γ快的又打印出下面的Log信息。
3.1.9 當(dāng)屏幕顯示Fragment_3時(shí)揩徊,點(diǎn)擊電源鍵關(guān)閉屏幕 <p>
當(dāng)屏幕正在展示某個(gè)Fragment時(shí)腰鬼,點(diǎn)擊了電源鍵或者手機(jī)自動(dòng)息屏?xí)r,這種情況下塑荒,會(huì)和以上的幾種情況有所不同熄赡,下面還是以Fragment_3為例來嘗試分析一下。
根據(jù)3.1.7的信息齿税,此時(shí)彼硫,F(xiàn)ragment_1處于9_onDestroyView()這個(gè)生命周期方法,F(xiàn)ragment_2凌箕,3拧篮,4此時(shí)3個(gè)Fragment都處于6_onResume()這個(gè)生命周期方法。
點(diǎn)擊電源鍵之后:
Fragment_2,3,4依次走了7_onPause(),8_onStop()牵舱。接下來串绩,再點(diǎn)亮屏幕,顯示Fragment_3:
Fragment_2,3,4依次走了5_onStart(),6_onResume()芜壁。
到了此時(shí)礁凡,ViewPager中Fragment滑動(dòng)的情況就結(jié)束了。這些情況慧妄,寫一個(gè)很簡(jiǎn)單的測(cè)試demo就可以搞的比較清楚了顷牌。要一次就記得清楚也不算特別現(xiàn)實(shí),多想幾次就可以了塞淹。
4. 嘗試分析問題2<p>
-
ViewPager的setOffscreenPageLimit()方法對(duì)Fragment有哪些影響韧掩?
直白翻譯就是設(shè)置幕后頁面限制 :) 。其實(shí)就是設(shè)置緩存在ViewPager中的Fragment的數(shù)量窖铡。
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中源碼可以看出疗锐,傳入的參數(shù)如果小于DEFAULT_OFFSCREEN_PAGES(就是1)這個(gè)值是無效的。所以嘗試使用setOffscreenPageLimit(0)來關(guān)閉ViewPager的預(yù)加載是無效费彼。也就是說滑臊,limit只有大于等于2時(shí)才會(huì)有效。Ps:ViewPager調(diào)用這個(gè)方法后箍铲,里面populate()這個(gè)方法又做了啥雇卷,我目前想關(guān)心也關(guān)心不了,大概看了下,ViewPager共有3000多行代碼关划,我目前的代碼閱讀能力還很低小染,即使想做到只是閱讀相關(guān)代碼也困難,目前想讀通比較困難贮折,以后代碼閱讀能力提升再來看了裤翩。
接下來,在ViewPager中调榄,加入viewPager.setOffscreenPageLimit(2)
這行代碼踊赠,再次運(yùn)行,看看Log信息
根據(jù)問題1的分析每庆,看到這個(gè)Log信息就很好理解了筐带,多了Fragment_3的Log信息,而且走的方法和Frgment_1和2是一樣的缤灵。后面的情況的Log信息便不再貼出來了伦籍,本質(zhì)是一樣的,只是多預(yù)加載了一個(gè)Fragment腮出。但此時(shí)ViewPager依然只是保留3個(gè)Fragment的信息鸽斟。當(dāng)滑到Fragment_4的時(shí)候,F(xiàn)ragment_1走了7_onPause(),8_onStop(),9_onDestroyView()利诺。Fragment_2,3,4則處于6_onResume()富蓄。
這個(gè)方法對(duì)Fragment生命周期方法的調(diào)用順序上并沒有什么影響,只是預(yù)加載的Fragment的數(shù)量受設(shè)置的limit參數(shù)影響慢逾。
5. 嘗試分析問題3 <p>
- 在ViewPager中,Fragment的setUserVisibleHint()對(duì)Fragment的生命周期有哪些影響立倍?
再次顧名思義:設(shè)置用戶可見暗示 。:) 這個(gè)方法可以用來判斷Fragment是否可見侣滩。這里也不再貼源碼了口注。
5.1 Fragment加入setUserVisibleHint() <p>
修改代碼前,我把在分析問題2時(shí)加入的viewPager.setOffscreenPageLimit(2)
注釋掉君珠。這樣Log信息會(huì)少一些寝志。在每個(gè)Fragment中代碼做了一些修改加入了一個(gè)setUserVisibleHint()
。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
log("setUserVisibleHint");
}
再次運(yùn)行Demo策添,查看Log信息:
和預(yù)想的大不一樣材部。setUserVisibleHint()竟然最先被調(diào)用。預(yù)想的是1_onAttach()執(zhí)行后唯竹,才會(huì)走setUserVisibleHint()這個(gè)方法乐导。而且,F(xiàn)ragment_1走了兩次setUserVisibleHint()這個(gè)方法浸颓。其他的Log信息倒是比較熟悉了物臂。
滑動(dòng)屏幕旺拉,滑動(dòng)到Fragment_2,看下此時(shí)的Log信息:
這時(shí)不僅先執(zhí)行了Fragment_3的setUserVisibleHint()棵磷,連Fragment_1,2的setUserVisibleHint()方法也比Fragment_3的1_onAttach()蛾狗。此時(shí),F(xiàn)ragment_1,2,3都處于6_onResume()仪媒。再由Fragment_2滑到Fragment_1沉桌,看下Log:
此時(shí)规丽,F(xiàn)ragment_1,2的setUserVisibleHint()方法優(yōu)先被調(diào)用蒲牧,F(xiàn)ragment_3走到9_onDestroyView()方法撇贺。
到了這里赌莺,感覺setUserVisibleHint()這個(gè)方法每次滑動(dòng)都會(huì)被調(diào)用,而且最先被調(diào)用松嘶,緩存在ViewPager中的Fragment都會(huì)被調(diào)用艘狭。
接下來模擬一下延遲加載網(wǎng)絡(luò)請(qǐng)求。
5.2 延遲加載翠订,模擬網(wǎng)絡(luò)請(qǐng)求 <p>
在每個(gè)Fragment中巢音,加入一個(gè)Boolean值,并將代碼做一些改動(dòng)尽超。
@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("開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了");
isCreate = false;
}
}
當(dāng)Fragment可見時(shí)官撼,進(jìn)行網(wǎng)絡(luò)請(qǐng)求。網(wǎng)絡(luò)請(qǐng)求加上了前提條件似谁。除了isVisibleToUser為true外還有一個(gè)isCreate傲绣。Fragment走了2_onCreate()iSCreate設(shè)為了true。我通常會(huì)用Fragment.newInstrance()這個(gè)方法進(jìn)行初始化Fragment時(shí)來傳值巩踏,在2_onCreate()方法中通過getArguments()來接收值秃诵,所以這里加了一個(gè)條件isCreate為true。
再次運(yùn)行Demo塞琼,看Log信息:
我擦,我的"開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了"呢彪杉?最關(guān)鍵的Log信息卻沒有看到毅往。根據(jù)5.1,每次滑動(dòng)時(shí)派近,肯定會(huì)調(diào)用setUserVisibleHint()這個(gè)方法的啊煞抬,可Fragment_1明明都已經(jīng)可見了,怎么沒有出現(xiàn)"開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了"构哺?于是革答,看了下源碼战坤,...,白看残拐,沒瞧出啥途茫。好吧,我再滑下屏幕,Fragment_2顯示溪食,看下Log:
滑到Fragment_2后,第一行倒是打印出來了"開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了"错沃。繼續(xù)滑栅组,F(xiàn)ragment_3,4也都打印出來了。然后我又依次從Fragment_4滑到了Fragment_1后枢析,Log信息也打出了
"E/Fragment_1: -------->開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了"
可為啥Fragment_1第一次顯示時(shí)玉掸,沒有打印呢?
這里結(jié)合5.1想了一下醒叁,setUserVisibleHint()這個(gè)方法在1_onAttach()方法之前司浪。Fragment_1第一次加載時(shí),2_onCreate()方法還沒走呢把沼,此時(shí)isCreate的值還是fasle啊易,就不會(huì)打印"開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了"。根據(jù)5.1的Log信息饮睬,F(xiàn)ragment_1兩次調(diào)用setUserVisibleHint()時(shí)租谈,isCreate都為false。而isCreate值變?yōu)閠rue后捆愁,卻不在執(zhí)行setUserVisibleHint()方法了割去。
暫時(shí)的解決思路在 6.2 給出
6. 嘗試分析問題4<p>
- 點(diǎn)擊TabLayout的Tab時(shí),F(xiàn)ragment經(jīng)歷的生命周期和滑動(dòng)ViewPager有啥不一樣牙瓢?
問題1,2,3都是通過滑動(dòng)來分析的劫拗,沒有通過點(diǎn)擊Tab。現(xiàn)在來看看點(diǎn)擊Tab矾克。
6.1點(diǎn)擊Tab時(shí)页慷,F(xiàn)ragment所走的生命周期 <p>
開始前,把每個(gè)Fragment中的setUserVisibleHint()先注釋掉,之后運(yùn)行Demo胁附。這里點(diǎn)擊Tab_2是體現(xiàn)不出啥效果的酒繁,因?yàn)镕ragment_2已經(jīng)預(yù)加載了。我這里點(diǎn)擊Tab_3控妻,看Log信息:
到了這里州袒,這個(gè)Log信息還是比較容易理解了。此時(shí)的Fragment_2,3,4處于6_onResume()這個(gè)方法弓候。Fragment_1走到9_onDestroyView()郎哭。和滑動(dòng)時(shí)其實(shí)沒有本質(zhì)的區(qū)別他匪。就是點(diǎn)擊可以從Fragment_1越過Fragment_2而直接到Fragment_3這種交互上的區(qū)別而造成的少了滑動(dòng)時(shí)對(duì)Fragment_3的預(yù)加載。如果是從Fragment_1滑到Fragment_3夸研,在滑到Fragment_2時(shí)邦蜜,就已經(jīng)完成了對(duì)Fragment_3的預(yù)加載。而通過點(diǎn)擊的方式時(shí)亥至,點(diǎn)擊Tab_3后悼沈,F(xiàn)ragment_3是從1_onAttach()這個(gè)生命周期方法開始的。
6.2 點(diǎn)擊Tab的方式和Fragment延遲加載遇到的問題 <p>
在5.2中遇到的一個(gè)問題就是姐扮,F(xiàn)ragment_1用了我寫的那種延遲加載的方法時(shí)絮供,第一次加載不會(huì)進(jìn)行網(wǎng)絡(luò)請(qǐng)求,只有再次滑到Fragment_1后才會(huì)進(jìn)行網(wǎng)絡(luò)請(qǐng)求茶敏。到了這里壤靶,同樣也會(huì)引起這個(gè)問題。少了預(yù)加載睡榆,例如6.1中萍肆,直接點(diǎn)擊Tab_3時(shí)袍榆,此時(shí)Fragment_3并沒有預(yù)加載胀屿。setUesrVisibleHint()的方法又比1_onAttach()方法調(diào)用的早。會(huì)有和5.2中遇到的問題一樣包雀。第一次點(diǎn)擊Tab_3后宿崭,并不會(huì)進(jìn)行網(wǎng)絡(luò)請(qǐng)求,只有再次滑動(dòng)到Fragment_3后才會(huì)進(jìn)行網(wǎng)絡(luò)請(qǐng)求才写。
修改每個(gè)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("開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求了");
isCreate = false;
isHasLaodOnce = true;
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
log(" 4__onActivityCreated");
load();
}
load()方法有3個(gè)前提條件葡兑,isCreate,可見赞草,沒有進(jìn)行網(wǎng)絡(luò)請(qǐng)求過讹堤。
根據(jù)5.1,setUserVisibleHint()這個(gè)方法會(huì)在預(yù)加載Fragment時(shí)厨疙,會(huì)在Fragment的1_onAttach()前調(diào)用洲守。此時(shí),在setUserVisibleHint()里面調(diào)用也不用害怕空指針的問題沾凄,因?yàn)橛?個(gè)前條件梗醇。當(dāng)通過滑動(dòng)ViewPger時(shí),根據(jù)5.1知道撒蟀,每次滑動(dòng)都會(huì)調(diào)用setUserVisibleHint()這個(gè)方法叙谨,進(jìn)行預(yù)加載后,當(dāng)滑到Fragment_2,3,4時(shí)保屯,就會(huì)調(diào)用里面的load()方法手负。
針對(duì)5.2和6.2的問題涤垫。解決辦法就是在Fragment的4_onCreateActivity()中調(diào)用load。我個(gè)人習(xí)慣把網(wǎng)絡(luò)請(qǐng)求放在這個(gè)生命周期竟终。在Fragment_1和點(diǎn)擊Tab時(shí)雹姊,引起問題的原因就是setUserVisibleHint()先于1_onAttach()調(diào)用,不能滿足前提條件中的isCreate衡楞,所以load方法不會(huì)被調(diào)用吱雏。而4_onCreateActivity()中會(huì)再次調(diào)用load()方法,此時(shí)還滿足3個(gè)前提條件瘾境。這樣歧杏,遺留的問題也解決了。ViewPager中Fragment延遲加載這個(gè)需求也可以實(shí)現(xiàn)了迷守。如果此時(shí)Fragment中有一個(gè)輪播圖的話犬绒,也可以通過getUserVisibleHint()這個(gè)方法來選擇關(guān)閉輪播圖線程的時(shí)機(jī)。
當(dāng)再次滑回已經(jīng)加載過的Fragment
兑凿,空白時(shí)凯力,是由于Adapter
的destroyItem()
將之前的Fragment
已經(jīng)清除
粗暴的做法是ViewPager 的 Adapter 的destroyItem()方法,需要重寫礼华,將super()方法注釋掉
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
}
但這比較適合Fragment
比較少的情況咐鹤,有3,4個(gè)Fragment
圣絮,因?yàn)榭赡軙?huì)比較占用內(nèi)存祈惶,這其實(shí)和使用setOffscreenPageLimit()
來保存Fragment
個(gè)數(shù)的效果是一樣
一旦Fragment
多了,除了單純的利用ViewPager
的特征外扮匠,還可以考慮結(jié)合使用 OkHttp
的緩存機(jī)制來做捧请。把兩者結(jié)合,效果應(yīng)該比單純使用ViewPager
要好
7. 總結(jié) <p>
這個(gè)周末用了一天半棒搜,寫好了這篇博客疹蛉,用來記錄下我的學(xué)習(xí)過程。寫的內(nèi)容也是比較淺顯力麸,水平太菜可款,如果能有能力閱讀源碼,對(duì)于問題2,3,4會(huì)有更深入的了解末盔,會(huì)有好的辦法筑舅,而不是這篇博客中折中的方法。如果有啥錯(cuò)誤陨舱,請(qǐng)趕緊指出翠拣。: )
如果你看到了這里,真愛啊游盲。十分感謝误墓。
共勉 :)