Android基礎——控件的混合生命週期

1、Activity下的Fragment生命週期

Android官方給出:基本狀況下Activity與Fragment的生命週期對應關係以下圖:
這裏寫圖片描述
圖1.0 靜態佈局狀況下fragment與Activity的生命週期對應圖css

這個生命週期的嚴格對應關係成立條件爲:fragment經過靜態佈局的方式插入到Activity的佈局中。可是經過FragmentManager動態管理的時候就有些許差異了。java


* FragmentManager動態管理Fragment的生命週期:*


這裏演示的是動態加載的Fragment(經過FMManager),其實經過xml靜態插入的Fragment基本生命週期根下面是同樣的,有些許不一樣(如上圖1.0⬆️)。不一樣的緣由是Activity裏面的onCreate()方法下會調用
到setContentView()加載靜態佈局,這個方法會按照深度優先的算法解析xml裏面的View樹結構,加載嵌套在裏面的的Fragment以及View。而動態加載的Fragment的生命週期整體時依附於Activity,可是因爲不會在
setContentView()中獲得解析構造,所以生命週期前面部分(onAttach、onCreate、onCreateView、onActivityCreated()、onStart、onResume)未與用戶交互的階段,這些週期調用時機隨着在Activity生命週期的不一樣時刻
經過FragmentManager加載會有不一樣的表現。web

假設:Activity:A。 Fragment: F一、F2 經過FragmentManager 在onCreate()方法進行Fragment的add。算法

(1)Fragment自身生命週期

階段一:啓動時
A-onCreate()
F1-onAttach()
F1-onCreate()
F2-onAttach()
F2-onCreate()
F1-onCreateView()
F1-onViewCreated()
F1-onActivityCreated()
F2-onCreateView()
F2-onViewCreated()
F2-onActivityCreated()\
F1-onStart()
F2-onStart()
A-onStart()
A-onResume()
F1-onResume()
F2-onResume()
A-onAttachedToWindow()
階段二:按Home/菜單鍵
F1-onPause()
F2-onPause()
A-onPause()
F1- onSaveInstanceState()
F2-onSaveInstanceState()
A-onSaveInstanceState()
F1-onStop()
F2-onStop()
A-onStop()
階段三:從階段二返回
A-onRestart()
F1-onStart()
F2-onStart()
A-onStart()
A-onResume()
F1-onResume()
F2-onResume()
階段四:直接按下Back鍵返回
F1-onPause()
F2-onPause()
A-onPause()
F1-onStop()
F2-onStop()
A-onStop()
F1-onDestroyView()
F1-onDestroy()
F1-onDetach()
F2-onDestroyView()
F2-onDestroy()
F2-onDetach()
A-onDestroy()
A-onDetachedFromWindow()。

能夠看出,與圖1.0所示的生命週期對應基本同樣。緩存

(2)Fragment自身生命週期對View生命週期的影響

任何一個View也是有生命週期的,從構造——>onFinishInflate()——>attachToWindow() ——>onMesure()——>onLayout()——>onDraw——>響應事件交互——>onDetachFromWindow()。svg

假設有兩個View:V一、V2。V1在Activity A中,V2在Fragment F中。 View在xml佈局中,Fragment經過FragmentManager加載。佈局

狀況1、在啓動到可交互時:spa

//Activity建立
A-onCreate()——>
//Activity的xml靜態佈局中,按照深度優先順序先對view解析
V1-onFinishInflate()——>
//建立Fragment
F1-onAttach()——>
F-onCreate()——>
//建立Fragment視圖
F-onCreateView()——>
//解析Fragment裏面的view視圖
V2-onFinishInflate()——>
F1-onViewCreated()——>
F-onActivityCreated()——>
F-onStart()——>
A-onResume()——>
F-onResume()——>
//Activity、View將要界面將要繪製顯示
A-onAttachedToWindow()——>
V1-onAttachedToWindow()——>
V2-onAttachedToWindow()——>
//View視圖繪製、按照V一、V2加載順序
V1-onMeasure()——>
V2-onMeasure()——>
V1-onLayout()——>
V2-onLayout()——>
V1-onDraw()——>
V2-onDraw()

狀況2、當銷燬時code

//Fragment生命週期跟隨Activity生命週期銷燬變化而變化
F-onPause()——>
A-onPause()——>
F-onStop()——>
A-onStop()——>
//Fragment視圖銷燬
F-onDestroyView()——>
//因爲Fragment是動態加載,所以view由FragmentManager管理,此時跟隨Fragment視圖一同銷燬
V2-onDetachedFromWindow()——>
F-onDestroy()——>
F-onDetach()——>
A-onDestroy()——>
//因爲V1是靜態插入Activity佈局文件裏面,所以它的生命結束跟隨Activity視圖銷燬而銷燬
V1-onDetachedFromWindow()——>
A-onDetachedFromWindow()

總結:View在Activity中和在Fragment中有兩個點不太同樣:xml

  • inflate:簡單來講,不管fragment是經過源代碼FragmentManager添加仍是經過xml佈局插入,Activity的View在setContentView()時被Inflate,Fragment的View在onCreateView時被inflate;

  • onAttachedToWindow():這個View的生命週期的調用不管在fragment、Activity都同樣,由於Activity纔是整個界面的承載者和入口,所以Activity的onAttachedToWindow處於使得window處於前臺以後,View的添加
    就會調調用自身的onAttachedToWindow

  • onDetachFromWindow():當銷燬時,狀況稍微不一樣:若是fragment、view都在xml文件中插入,則V一、V2都在Activity的onDetachedFromWindow時觸發。可是當fragment時經過FragmentManager中添加的時候,Activity的View在Activity調用onDetachedFromWindow()時會觸發,而Fragment裏面的View會在Fragment的onDestroyView()時觸發。


2、Activity+ ViewPager+ Fragment+ View的生命週期


ViewPager也是一個View,Fragment根ViewPager的結合其實也是經過FragmentManager來管理的,所以歸結來講仍是經過動態加載的方式來管理Fragment的生命週期。

假設:
含有ViewPager的Activity:A;
Fragment:F一、F二、F三、F4,經過FM交ViewPagerAdapter。
嵌在Fragment中的View:V一、V二、V三、V4

(1)啓動Activity:

//Activity的啓動
A-onCreate()——>
A-onStart()——>
A-onResume()——>
A-onAttachedToWindow()——>
//不同的是在ViewPager顯示後才加載Fragment
//按照getItem中的順序加載
F1-onAttach()——>
F1-onCreate()——>
F2-onAttach()——>
F2-onCreate()——>
//加載F1視圖
F1-onCreateView()——>
V1-onFinishInflate()——>
V1-onAttachedToWindow()——>
F1-onViewCreated()——>
F1-onActivityCreated()——>
F1-onStart()——>
F1-onResume()——>
//加載F2視圖
F2-onCreateView()——>
V2-onFinishInflate()——>
V2-onAttachedToWindow()——>
F2-onViewCreated()——>
F2-onActivityCreated()——>
F2-onStart()——>
F2-onResume()——>
//按照F一、F2的順序繪製V一、V2
V1-onMeasure()——>
V2-onMeasure()——>
V1-onLayout()——>
V2-onLayout()

從上面咱們能夠看出
第一,與fragment被添加至Acvitity的時候不同,fragment的構造是在Activity調用onAttachedToWindow()以後;
第二,預先加載了一個fragment,而且生命週期執行到了onResume()。

(2)滑動Fragment翻頁:

關於這個的所有狀況就不放在上面了,下面的例子是使用FragmentPagerAdapter說明的,FragmentStatePagerAdapter在是否銷燬不保存Fragment上有所不一樣。默認狀況下FragmentPagerAdapter會預加載/保留包括當前fragment的先後3個fragment(若是有的話)的狀態,便於切換界面的時候更加流暢。同時,對於未保留狀態的其餘fragment並未銷燬,只是執行到了onDestroyView()方法,將其內部的view銷燬,從新返回加載的時候便會從onCreateView()進行,而不會從新調用整個生命週期。

所以對於ViewPager下的Fragment的狀態保存通常在onDestroyView以前保存下來,用於下一次的初始化(FragmentStatePagerAdapter則不會銷燬全部不保存狀態的Fragment)。
下面的例子是使用FragmentPagerAdapter。

一、從第二個Fragment切換到第三個Fragment

//預加載第四個Fragment
F4-onAttach()
F4-onCreate()
//銷燬第一個Fragment的視圖
F1-onPause()
F1-onStop()
F1-onDestroyView()
V1-onDetachedFromWindow()
//建立第一個fragment視圖,繼續生命週期回調
F4-onCreateView()
V4-onFinishInflate()
V4-onAttachedToWindow()
F4-onViewCreated()
F4-onActivityCreated()
F4-onStart()
F4-onResume()
//View視圖繪製
V4-onMesure()
V4-onLayout()
V4-onDraw()

二、從第三個Fragment切換到第二個Fragment

//恢復第一個Fragment的視圖
F1-onCreateView()
V1-onFinishInflate()
V1-onAttachToWindow()
F1-onViewCreated()
F1-onActivityCreated()
//銷燬第四個不在保存範圍內的Fragment視圖
F4-onPause()
F4-onStop()
F4-onDestroyView()
V4-onDetachedFromWindow()
//繼續生命週期回調
F1-onStart()
F1-onResume()
//View視圖繪製
V1-onMesure()
V1-onLayout()
V1-onDraw()

(2.1)直觀地比較FragmentPagerAdapter和FragmentStatePagerAdapter的區別

網上挖出來的圖,很直觀。
FragmentPagerAdapter的Fragment管理:
這裏寫圖片描述

FragmentStatePagerAdapter的Fragment管理:

這裏寫圖片描述

默認狀況下,ViewPager保留三個Frament的狀態,可是經過setOffscreenPageLimit(int limit),能夠調整預加載/緩存的數量,預先加載數 = limit,緩存數目= 2*limit +1 :

//默認的緩存頁面數量(常量)
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

//緩存頁面數量(變量)
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;

public void setOffscreenPageLimit(int limit) {
    //當咱們手動設置的limit數小於默認值1時,limit值會自動被賦值爲默認值1(即DEFAULT_OFFSCREEN_PAGES)
    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) {
        //通過前面的攔截判斷後,將limit的值設置給mOffscreenPageLimit,用於
        mOffscreenPageLimit = limit;
        populate();
    }
}

(3)退出Activity:

退出Activaty的時候,會按照ViewPager添加Fragment的順序依次銷燬Fragment,調用(Fragment.onDestoyView()——>View.onDetachFromWindow()——>Fragment.onDestroy()——>Fragment.onDetach(),對於未保存狀態的Fragment,則直接從onDestroy()——>onDetach() )。

3、小結

  • 靜態加載:xml佈局內容屬於靜態加載,靜態加載的View控件是跟隨Activity的生命週期的。在Activity的setContentView()中按照深度優先的方式解析View樹,建立View對象,在Fragment的onCreateView()中解析View樹,而後將其view嵌至Fragment的fl_container中。靜態加載的控件內容都是由Activity來管理,所以在Activity的xml佈局中聲明的View的detachFromWindow是在Activity調用detachFromWindow的時候調用,包括靜態嵌入的fragment中的View。

  • 動態加載:代碼中加載(FMManager、addView等)屬於動態加載,動態加載的方式下View的生命週期由其管理者來肯定(容器ViewGroup經過addView、removeView會觸發View的attachFromWindow和detachFromWindow)。這種狀況下Fragment、View的生命週期都是由FragmentManager(對於View也能夠是它的父容器ViewGroup)來管理,所以當Fragement銷燬時,其內部的view會在它調用destroyView的時候觸發detachFromWindow事件,而View會在其ViewGroup容器調用removeView的時候觸發detachFromWindow。