Fragment

Fragment

Fragment是Android的視圖生命週期控制器(帶生命週期的自定義View),是Activity上的View層級中的一部分,通常能夠把它看作一個輕量級的Activity.與傳統的Activity相比,它只佔用更少的資源,而且提供更大的編碼靈活性、在超低版本上的兼容性等.java

使用 Fragment 能獲得較好的運行效果,而且能將過渡動畫兼容到更低的版本(經過FragmentTransition指定)。android

單Activity多Fragment模式.使用這種模式有許多好處:api

  • 首先第一個好處就是流暢,要知道Activity屬於系統組件,受AMS管理而且自身是一個God Object(上帝對象,Activity的功能太過強大以致於耦合了View層和Model層),它的開銷是很大的,單Activity模式能夠爲咱們節省不少資源,還能夠避免資源不足時,被前臺Activity覆蓋的Activity被殺掉致使頁面數據丟失的狀況(由於只有一個Activity,除非JAVA堆內存到達系統要殺掉一個程序的臨界點,不然系統最不傾向於殺死前臺正在運行的Activity);
  • 其次就是能夠將業務邏輯拆分紅更小的模塊,並將其組合複用,這在這在大型軟件系統中尤其重要(新版知乎就使用了單Activity多Fragment這種模式),由於咱們都知道Activity的是沒法在多個頁面中複用的,而此時Fragment就有了它的用武之地,它做爲輕量級的Activity,基本能夠代理Activity的工做,而且他是可複用
  • 再者,使用Fragment能夠爲程序帶來更大的靈活性,咱們都知道在Activity之間傳遞對象,對象須要序列化,這是由於Activity做爲系統組件,是受AMS管理的,而AMS屬於系統進程,不在當前程序運行的進程中,啓動Activity時須要暫時離開當前進程去到AMS的進程中,而AMS則會將你準備好的數據(也就是Intent之類的)用來啓動Activity,這也是Fragment和Activity之間的區別之一,Activity屬於系統組件,能夠在別的進程運行(組件化/多進程方案),而Fragment只是框架提供給咱們的的一個組件,它必須依附於Activity生存,而且只能在當前進程使用,但這同時也意味這它能夠得到更大的靈活性,咱們能夠給Fragment傳遞對象而無需序列化,甚至能夠給Fragment傳遞View之類的對象,這都是Activity不容易作到的.

Fragment使用

Fragment有兩種方式生成,一是硬編碼到xml文件中,二是在Java代碼中new,而後經過FragmentManager#beginTransaction開啓FragmentTransaction提交來添加Fragment.兩種方式存在着必定區別.硬編碼到xml的Fragment沒法被FragmentTransition#remove移除,與Activity同生共死,因此你要是這麼用了,就不用試了,移除不了的,可是在代碼中new出來的是能夠被移除的.數組

直接硬編碼到xml中:bash

<fragment
    android:id="@+id/map_view"
    android:name="org.kexie.android.dng.navi.widget.AMapCompatFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
複製代碼

添加Fragment的第二種方式就是使用FragmentManager#beginTransaction(代碼以下)動態添加,你須要先new一個Fragment,而後經過下面Fragment#requireFragmentManager獲取FragmentManager來使用beginTransaction添加Fragment,注意add方法的第一個參數,你須要給它指定一個id,也就是Fragment容器的id,一般容器是一個沒有子View的FrameLayout,它決定了這個Fragment要在什麼位置顯示.app

//在xml中編寫放置Fragment位置的容器
<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
    
//在java代碼中動態添加Fragment
requireFragmentManager()
        .beginTransaction()
        .add(R.id.fragment_container, fragment)
        .runOnCommit(()->{/*TODO*/})
        .addToBackStack(null)
        .commit();
複製代碼

在Fragment中,咱們可使用getId()能夠返回自身的id,一般用這個方法返回它所在的容器的id,供其餘Fragment添加進也添加到當前容器時使用(例如使用Fragment返回棧的場景)。框架

/**
 * Return the identifier this fragment is known by.  This is either
 * the android:id value supplied in a layout or the container view ID
 * supplied when adding the fragment.
 */
final public int getId() {
    return mFragmentId;
}
複製代碼

須要注意的是FragmentTransaction並非當即執行的,而是在當前代碼執行完畢後,回到事件循環(也就是大家知道的Looper)時,纔會執行,不過他會保證在下一幀渲染以前獲得執行(經過Handler#createAsync機制),若要在FragmentTransaction執行時搞事情,你須要使用runOnCommit,在上面的代碼中我使用了Java8的lambda表達式簡寫了Runnable.異步

若是你還想使用Fragment回退棧記得調用addToBackStack,最後別忘了commit,這樣纔會生效,此時commit函數返回的是BackStackEntry的idide

固然FragmentTransaction不止能夠執行add操做,一樣也能夠執行remove,show,hide等操做.函數

onBackPressed在哪?我知道第一次使用Fragment的人確定都超想問這個問題.衆所周知Fragment自己是沒有onBackPressed的.不是Google不設計,而是真的無法管理啊!!!,若是一個界面上有三四個地方都有Fragment存在,一按回退鍵,誰知道要交給哪一個Fragment處理呢?因此Fragment自己是沒有onBackPressed的.可是,實際上給Fragment添加相似onBackPressed的功能的辦法是存在的,只是Google把它設計成交給開發者自行管理了.

這個功能是徹底基於Google的appcompat包實現的,可是如果咱們想要使用這個功能,可能須要較高版本的appcompat包,或者你把項目遷移到AndroidX(遷移方式下面會介紹).

咱們可使用FragmentActivity(AppCompatActivity繼承了FragmentActivity)的addOnBackPressedCallback方法爲你的Fragment提供攔截OnBackPressed的功能了.(非AndroidX的其餘版本可能也有實現了這個功能)

public void addOnBackPressedCallback(@NonNull LifecycleOwner owner, 
        @NonNull OnBackPressedCallback onBackPressedCallback)
複製代碼

OnBackPressedCallback#handleOnBackPressed須要返回一個boolean值。若是你在這個回調裏攔截了onBackPressed應該返回true,說明你本身已經處理了本次返回鍵按下的操做,這樣你的Fragment就不會被彈出返回棧了。

值得注意的是,這個函數的第一個參數,一個LifecycleOwner,Activity和Fragment都是LifecycleOwner,用於提供組件的生命週期,這個參數能夠幫咱們自動管理OnBackPressedCallback回調,你無需手動將他從Activity中移除,在LifecycleOwner的ON_DESTROY事件來到的時候,他會被自動移除列表,你無需擔憂內存泄漏,框架會幫你完成這些事情。

/**
 * Interface for handling {@link ComponentActivity#onBackPressed()} callbacks without
 * strongly coupling that implementation to a subclass of {@link ComponentActivity}.
 *
 * @see ComponentActivity#addOnBackPressedCallback(LifecycleOwner, OnBackPressedCallback)
 * @see ComponentActivity#removeOnBackPressedCallback(OnBackPressedCallback)
 */
public interface OnBackPressedCallback {
    /**
     * Callback for handling the {@link ComponentActivity#onBackPressed()} event.
     *
     * @return True if you handled the {@link ComponentActivity#onBackPressed()} event. No
     * further {@link OnBackPressedCallback} instances will be called if you return true.
     */
    boolean handleOnBackPressed();
}
複製代碼

咱們能夠看到Activity內管理的OnBackPressedCallback的執行循序與添加時間有關.最後被添加進去的能最早獲得執行.

public void addOnBackPressedCallback(@NonNull LifecycleOwner owner,
        @NonNull OnBackPressedCallback onBackPressedCallback) {
    Lifecycle lifecycle = owner.getLifecycle();
    if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
        // Already destroyed, nothing to do
        return;
    }
    // Add new callbacks to the front of the list so that
    // the most recently added callbacks get priority
    mOnBackPressedCallbacks.add(0, new LifecycleAwareOnBackPressedCallback(
            lifecycle, onBackPressedCallback));
}
複製代碼

能夠看到它是添加到mOnBackPressedCallbacks這個List的最前面的.

startFragmentForResult方法在哪?對不起和OnBackPressed同樣,Google沒有直接爲咱們實現這個方法,但這並不表明Fragment沒有這個功能,你固然能夠直接用定義getter的方式來獲取Fragment上內容,但這並非最佳實踐,爲了規範編碼咱們最好仍是使用公共的API

Fragment#setTargetFragment能夠給當前Fragment設置一個目標Fragment和一個請求碼

public void setTargetFragment(@Nullable Fragment fragment, int requestCode)
複製代碼

噹噹前Fragment完成相應的任務後,咱們能夠這樣將返回值送回給咱們的目標Fragment經過Intent

getTargetFragment().onActivityResult(getTargetRequestCode(),
Activity.RESULT_OK,new Intent());
複製代碼

不過要注意,目標Fragment和被請求的Fragment必須在同一個FragmentManager的管理下,不然就會報錯

好了若是你如今使用的appcompat包沒有上面的騷操做.那麼下面我將帶你遷移到AndroidX.

這裏可能有人會問AndroidX是什麼?

簡單來說AndroidX就是一個與平臺解綁的appcompat(低版本兼容高版本功能)庫,也就是說在build.gradle中不須要再與compileSdkVersion寫成同樣,例如以前這樣的寫法:

compile 'com.android.support:appcompat-v7:24.+'
複製代碼

(注:使用24.+則代表使用 24. 開頭的版本的最新版本,若直接使用+號則代表直接使用該庫的最新版本。

如今能夠寫成:

implementation 'androidx.appcompat:appcompat:1.1.0-alpha02'
複製代碼

(注:新的依賴方式implementation與compile功能相同,可是implementation沒法在該模塊內引用依賴的依賴,但compile能夠,這麼作的好處是能夠加快編譯速度。新的依賴方式api與compile徹底相同,只是換了名字而已)

在Android Studo3.0以上中的Refactor->Migrate to AndroidX的選點擊以後便可將項目遷移到AndroidX,在確認的時會提示你將項目備份以避免遷移失敗時丟失原有項目,一般狀況下不會遷移失敗,只是遷移的過程會花費不少的時間,若是項目很大,遷移時間會很長,這時即便Android Studio的CPU利用率爲0也不要關閉, 可是若是發生遷移失敗,這時候就須要手動遷移了。

一些使用gradle依賴的一些第三方庫中的某些類可能繼承了android.support.v4包下的Fragment,但遷移到AndroidX後appcompat的Fragment變成了androidx.fragment.app包下,原有的代碼下會畫紅線,Android Studio也會警告你出現錯誤,可是不用擔憂,依然能夠正常編譯,Android Studio在編譯的時候會自動完成基類的替換,但前提是你要確保你項目裏的gradle.properties進行了以下設置。

android.useAndroidX=true

android.enableJetifier=true
複製代碼

爲了消除這些難看的紅線,你能夠直接將新的Fragment使用這種方式強制轉換成原有的Fragment。

TextureSupportMapFragment mapFragment = TextureSupportMapFragment
.class.cast(getChildFragmentManager()
.findFragmentById(R.id.map_view));
複製代碼

同理,也能夠將舊的Fragment強制類型轉換成新的Fragment.

Fragment f = Fragment.class.cast(mapFragment);
複製代碼

(注:上面的TextureSupportMapFragment是一個典型案例,他是高德地圖SDK中的Fragment,它自己已經繼承了v4包下的Fragment,能夠用過上面的轉換來使他兼容AndroidX)

最後補充一個小Tips:當咱們在使用Fragment#getActivity()時返回的是一個可空值,若是沒有判空檢查在Android Studio中將會出現一個噁心的黃色警告,你可使用requireActivity()來代替它,一樣的方法還有requireFragmentManager()等.

Fragment生命週期

Fragment擁有Activity全部的生命週期回調函數而且因爲自身特色還擴展了一些回調函數,若是不熟悉Fragment,很容易憑直覺形成誤會.例如,一個Fragment並不會由於在Fragment回退棧上有其餘Fragment把它蓋住,又或者是你使用FragmentTransition將它hide而致使他onPause,onPause只跟此Fragment依附的Activity有關,這在Fragment的源碼中寫得清清楚楚.

/**
 * Called when the Fragment is no longer resumed.  This is generally
 * tied to {@link Activity#onPause() Activity.onPause} of the containing
 * Activity's lifecycle. */ @CallSuper public void onPause() { mCalled = true; } 複製代碼

那當咱們想在Fragment不顯示時作一些事情要怎麼辦呢?咱們有onHiddenChanged回調,當Fragment的顯示狀態經過FragmentTransition改變時(hide和show),就會回調這個函數,參數hidden將告訴你這個Fragment如今是被隱藏仍是顯示着.

/**
 * Called when the hidden state (as returned by {@link #isHidden()} of
 * the fragment has changed.  Fragments start out not hidden; this will
 * be called whenever the fragment changes state from that.
 * @param hidden True if the fragment is now hidden, false otherwise.
 */
public void onHiddenChanged(boolean hidden) {
}
複製代碼

經常使用的回調有這些:

  • onInflate(Context,AttributeSet,Bundle)只有硬編碼在xml中的Fragment(即便用fragment標籤)纔會回調此方法,這與自定義View十分相似,在實例化xml佈局時該方法會被調用,先於onAttach.

  • onAttach(Context)執行該方法時,Fragment與Activity已經完成綁定,當一個Fragment被添加到FragmentManager時,若是不是在xml中直接定義fragment標籤,那麼該方法老是最早被回調.該方法傳入一個Context對象,實際上就是該Fragment依附的Activity.重寫該方法時記得要調用父類的super.onAttach,父類的onAttach調用返回後,此時調用getActivity將不會返回null,可是Activity#onCreate可能還有沒有執行完畢(若是是在xml中定義,這種狀況就會發生,由於此時這個回調的這個發生的時間也就是你在Activity#onCreate裏setContentView的時間,直到Fragment#onViewCreated返回以後,Activity#onCreate纔會繼續執行)。

  • onCreate(Bundle)用來初始化Fragment。它老是在onAttach執行完畢後回調,可經過參數savedInstanceState獲取以前保存的值,記得必定要調用父類的super.onCreate。

  • onCreateView(LayoutInflater,ViewGroup,Bundle)須要返回一個View用來初始化Fragment的佈局,它老是在onCreate執行完畢後回調。默認返回null,值得注意的是,若返回null Fragment#onViewCreated將會被跳過,且若是是在xml中定義fragment標籤並用name指定某個Fragment,則這個方法不容許返回null,不然就會報錯。當使用ViewPager+Fragment時此方法可能會被屢次調用(與Fragment#onDestroyView成對調用)。

  • onActivityCreated(Bundle)執行該方法時,與Fragment綁定的Activity的onCreate方法已經執行完成並返回,若在此方法以前與Activity交互交互沒有任何保證,引用了未初始化的資源就會應發空指針異常。

  • onStart()執行該方法時,Fragment所在的Activity由不可見變爲可見狀態

  • onResume()執行該方法時,Fragment所在的Activity處於活動狀態,用戶可與之交互.

  • onPause()執行該方法時,Fragment所在的Activity處於暫停狀態,但依然可見,用戶不能與之交互,好比Dialog蓋住了Activity

  • onStop()執行該方法時,Fragment所在的Activity徹底不可見

  • onSaveInstanceState(Bundle)保存當前Fragment的狀態。該方法會自動保存Fragment的狀態,好比EditText鍵入的文本,即便Fragment被回收又從新建立,同樣能恢復EditText以前鍵入的文本,說實話我不太喜歡這個方法,保存到Bundle裏的設計實在是太蠢了,不過好在如今已經有了代替它的方案,Google的Android Jetpack MVVM框架,以後我也會專門出一篇文章來介紹。

  • onDestroyView()銷燬與Fragment有關的視圖,但未與Activity解除綁定,通常在這個回調裏解除Fragment對視圖的引用。一般在ViewPager+Fragment的方式下會使用並重寫此方法,而且與Fragment#onCreateView同樣多是屢次的。

  • onDestroy()銷燬Fragment。一般按Back鍵退出或者Fragment被移除FragmentManager時調用此方法,此時應該清理Fragment中所管理的全部數據,它會在onDetach以前回調。

  • onDetach()解除與Activity的綁定。在onDestroy方法以後調用。Fragment生命週期的最末期,若在super.onDetach返回後getActivity(),你將會獲得一個null。

Fragment管理

使用 FragmentManager 執行的操做包括:

  • 經過 findFragmentById()(對於在 Activity 佈局中提供 UI 的片斷)或findFragmentByTag()(對於提供或不提供 UI 的片斷)獲取 Activity 中存在的片斷
  • 經過 popBackStack()(模擬用戶發出的 Back 命令)將片斷從返回棧中彈出
  • 經過 addOnBackStackChangedListener() 註冊一個偵聽返回棧變化的偵聽器
  • 經過 add()添加一個Fragment
  • 經過 remove() 從Activity中移除一個Fragment
  • 經過 replace() 使用另外一個Fragment替換當前的,實際上就是remove()而後add()的結合
  • 經過 hide() 隱藏Fragment,其實是設置依附的ViewGroup隱藏,即View.Gone
  • 經過 show() 顯示隱藏的Fragment,其實是設置依附的ViewGroup顯示,即View.VISIBLE
  • 經過 commit() 真正的操做Fragment狀態

Fragment 與 Activity 通訊

Activity -> Fragment

public class FragmentActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        ExampleFragment fragment = new ExampleFragment();
        // 建立參數
        Bundle bundle = new Bundle();
        bundle.putString("argKey", "value");
        fragment.setArguments(bundle);
        getFragmentManager().beginTransaction()
        .add(R.id.fragment_container, fragment)
        .commit();

        // 略...
        fragment.apiFuncation();
    }
}

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 獲取
        Bundle bundle = getArguments();
        String arg = bundle.getString("argKey");
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}
複製代碼

Activity <- Fragment

在Fragment能夠經過getActivity()來獲取Fragment所依附的Activity實例,Activity也能夠經過 findFragmentById() 或 findFragmentByTag()來獲取對應的Fragment。

Fragment的加載過程

使用getSupportFragmentManager()方法獲取FragmentManager,當前Activity就必須繼承於FragmentActivity以及其子類。

// FragmentActivity類:
    final FragmentController mFragments = FragmentController.createController(new FragmentActivity.HostCallbacks());
    
    public FragmentManager getSupportFragmentManager() {
        return this.mFragments.getSupportFragmentManager();
    }
    // 內部類
    class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
        public HostCallbacks() {
            super(FragmentActivity.this);
        }
        ......
    }
    
// FragmentController類:
    private final FragmentHostCallback<?> mHost;

    public static FragmentController createController(FragmentHostCallback<?> callbacks) {
        return new FragmentController(callbacks);
    }

    private FragmentController(FragmentHostCallback<?> callbacks) {
        this.mHost = callbacks;
    }
    
    public FragmentManager getSupportFragmentManager() {
        return this.mHost.getFragmentManagerImpl();
    }
    
// FragmentHostCallback類:
    FragmentHostCallback(@NonNull FragmentActivity activity) {
        // 關注一下第三個參數activity.mHandler
        this(activity, activity, activity.mHandler, 0);
    }

    FragmentHostCallback(@Nullable Activity activity, @NonNull Context context, @NonNull Handler handler, int windowAnimations) {
        // 初始化了FragmentManagerImpl類
        this.mFragmentManager = new FragmentManagerImpl();
        this.mActivity = activity;
        this.mContext = (Context)Preconditions.checkNotNull(context, "context == null");
        // 初始化了Handler,該mHandler會在commit操做中用到
        this.mHandler = (Handler)Preconditions.checkNotNull(handler, "handler == null");
        this.mWindowAnimations = windowAnimations;
    }
    
    FragmentManagerImpl getFragmentManagerImpl() {
        return this.mFragmentManager;
    }
    
// FragmentManagerImpl類:
    final class FragmentManagerImpl extends FragmentManager implements Factory2
複製代碼
  • getSupportFragmentManager()方法中,出現了mFragments
  • mFragments實際爲FragmentController,而且將FragmenActivity的內部類HostCallbacks經過構造參數傳入該類中,並保存到成員變量mHost中
  • HostCallbacks爲FragmentActivity的內部類,其繼承於類FragmentHostCallback。而且HostCallbacks構造方法中也同時調用了其父類FragmentHostCallback的構造方法,並初始化了類FragmentManagerImpl,而FragmentManagerImpl繼承於FragmentManager
  • 回到第1步的方法getSupportFragmentManager()中,該方法裏調用了mFragments.getSupportFragmentManager()方法
  • mFragments.getSupportFragmentManager()方法裏又調用了本部分的最後一步mHost.getFragmentManagerImpl()方法中,第2步講到後面會使用。而且最後返回第3步所說的實例化的FragmentManagerImpl類

咱們能夠很清晰的看到mFragments被final修飾並且又是類成員,所以一個FragmentActivity對應一個FragmentController、一個FragmentHostCallback和一個FragmentManagerImpl。

// FragmentManagerImpl類:
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }
    
// BackStackRecord類:
    final class BackStackRecord extends FragmentTransaction
                    implements BackStackEntry, OpGenerator {
        ......
        public BackStackRecord(FragmentManagerImpl manager) {
            this.mManager = manager;
        }
        ...
    }
複製代碼

其實就是返回了繼承於FragmentTransaction的BackStackRecord類。注意啦BackStackRecord類很重要,很是重要。本文以android-28爲標準而講解,而本類在28中修改很大,28以前的版本BackStackRecord實現了Runnable接口,具體的可自行查看。該類直譯過來可被稱爲:返回堆棧記錄,此後的有關fragment的基本全部操做都是經過它來完成。

// BackStackRecord類:
    ArrayList<BackStackRecord.Op> mOps = new ArrayList();

    public FragmentTransaction add(Fragment fragment, @Nullable String tag) {
        this.doAddOp(0, fragment, tag, 1);
        return this;
    }
    
    private void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
        // 獲取fragment的Class對象
        Class fragmentClass = fragment.getClass();
        // 獲取該fragment對象的修飾符
        int modifiers = fragmentClass.getModifiers();
        // 如該fragment爲:匿名類或者不是public修飾符標識的類或者(它是內部類且不是靜態的)
        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers) || fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)) {
            // 則拋出以下異常
            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName() + " must be a public static class to be properly recreated from" + " instance state.");
        } else {
            fragment.mFragmentManager = this.mManager;
            if (tag != null) {
                // 防止同一個fragment被add屢次而且設置了多個不一樣的tag
                // 若是tag不等於空,而且tag不等於fragment.mTag
                if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                    // 則拋出以下異常
                    throw new IllegalStateException("Can't change tag of fragment " + fragment + ": was " + fragment.mTag + " now " + tag);
                }

                fragment.mTag = tag;
            }

            if (containerViewId != 0) {
                // 容器視圖id,即FrameLayout佈局id
                if (containerViewId == -1) {
                    throw new IllegalArgumentException("Can't add fragment " + fragment + " with tag " + tag + " to container view with no id");
                }
                // 方法屢次add其containerViewId且設置多個不一樣的containerViewId
                if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                    throw new IllegalStateException("Can't change container ID of fragment " + fragment + ": was " + fragment.mFragmentId + " now " + containerViewId);
                }

                fragment.mContainerId = fragment.mFragmentId = containerViewId;
            }

            this.addOp(new BackStackRecord.Op(opcmd, fragment));
        }
    }
    
    void addOp(BackStackRecord.Op op) {
        this.mOps.add(op);
        op.enterAnim = this.mEnterAnim;
        op.exitAnim = this.mExitAnim;
        op.popEnterAnim = this.mPopEnterAnim;
        op.popExitAnim = this.mPopExitAnim;
    }
    
// BackStackRecord內部類Op:
    static final class Op {
        int cmd;//指令:表明對fragment的操做,好比:add==1
        Fragment fragment;//保存要操做的fragment
        // 如下四個爲動畫設置
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;

        Op() {
        }

        Op(int cmd, Fragment fragment) {
            this.cmd = cmd;
            this.fragment = fragment;
        }
    }
複製代碼

再次將此部分有關代碼放到了一塊兒,接着一步一步說明一下吧:

  • 從add操做開始接着調用自身的doAddOp(0, fragment, tag, 1)方法,並設置Op中的cmd指令爲1
  • doAddOp(0, fragment, tag, 1)方法中判斷一些異常狀況後設置fragment的成員屬性mContainerId和mFragmentId爲containerViewId;而且繼續執行自身的addOp(new BackStackRecord.Op(opcmd, fragment))方法。
  • addOp(new BackStackRecord.Op(opcmd, fragment))方法中,就是將Op對象加入到mOps集合中,並設置動畫。
// BackStackRecord類:
    public FragmentTransaction show(Fragment fragment) {
        this.addOp(new BackStackRecord.Op(5, fragment));
        return this;
    }
複製代碼

show操做其實只是調用addOp(new BackStackRecord.Op(5, fragment))方法,設置一下Op類的cmd指令。

// BackStackRecord類:
    public int commit() {
        return this.commitInternal(false); // 注意這個false哦
    }
    
    int commitInternal(boolean allowStateLoss) {
        // 若重複commit,則會拋出此異常
        if (this.mCommitted) {
            throw new IllegalStateException("commit already called");
        } else {
            if (FragmentManagerImpl.DEBUG) {
                Log.v("FragmentManager", "Commit: " + this);
                LogWriter logw = new LogWriter("FragmentManager");
                PrintWriter pw = new PrintWriter(logw);
                this.dump(" ", (FileDescriptor)null, pw, (String[])null);
                pw.close();
            }

            this.mCommitted = true;
            // 這個成員默認是false,只有咱們本身調用了addToBackStack方法,纔會設置爲true
            if (this.mAddToBackStack) {
                this.mIndex = this.mManager.allocBackStackIndex(this);
            } else {
                this.mIndex = -1;
            }
            // 此mManager咱們上面第一部分講解過,實際爲FragmentManagerImpl類
            this.mManager.enqueueAction(this, allowStateLoss);
            return this.mIndex;
        }
    }
    
// FragmentManagerImpl類:
    // 注意:參數action爲BackStackRecord類,而allowStateLoss爲false,上面有說明
    public void enqueueAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            // 狀態丟失的異常檢查(具體請參考:https://www.jianshu.com/p/aa735c60c867)
            // 若是咱們提交使用的是commit方法,則會走到該判斷裏來,由於commit方法allowStateLoss傳的是false。若是咱們提交使用的是commitAllowingStateLoss方法,則不會走到該判斷中來,由於commitAllowingStateLoss方法傳入的allowStateLoss爲true
            this.checkStateLoss();
        }

        synchronized(this) {
            if (!this.mDestroyed && this.mHost != null) {
                if (this.mPendingActions == null) {
                    this.mPendingActions = new ArrayList();
                }
                // 加入待定任務隊列中,mPendingActions是ArrayList
                this.mPendingActions.add(action);
                this.scheduleCommit();
            } else if (!allowStateLoss) {
                throw new IllegalStateException("Activity has been destroyed");
            }
        }
    }
    
    void scheduleCommit() {
        synchronized(this) {
            boolean postponeReady = this.mPostponedTransactions != null && !this.mPostponedTransactions.isEmpty();
            boolean pendingReady = this.mPendingActions != null && this.mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                this.mHost.getHandler().removeCallbacks(this.mExecCommit);
                this.mHost.getHandler().post(this.mExecCommit);
            }

        }
    }
複製代碼
  • ft.commit()操做實際上調用了BackStackRecord類自身的commitInternal方法,而後判斷是否設置了mAddToBackStack,最後又繼續調用了FragmentManagerImpl類中的enqueueAction方法繼續執行。
  • FragmentManagerImpl類中的enqueueAction方法中有兩個參數:action和allowStateLoss,而代碼中也說明了allowStateLoss爲false(詳細請看上面代碼部分),所以咱們主要須要關注的仍是action這個參數,action其實是FragmentManagerImpl類中的OpGenerator接口,而實現OpGenerator接口的地方有兩個:一個是FragmentManagerImpl類中的內部類PopBackStackState(後面講「回退棧」時再詳細說明),另外一個就是咱們所熟悉的BackStackRecord類。接着說流程:在enqueueAction方法中將咱們要操做的action添加到待定任務隊列中,繼續執行自身的scheduleCommit()方法。
  • 咱們能夠從代碼中看到scheduleCommit()方法中,調用了Handler的post方法,執行了一個任務mExecCommit,是否還記得mHost是誰?在上面第一部分實例化FragmentHostCallback類時傳入了FragmentActivity,繼而初始化了mHandler,而mHost正是FragmentHostCallback類型,而mHost.getHandler()返回的正是此時的mHandler。

從上面的三步中咱們瞭解到了,如今邏輯以及到了mExecCommit(Runnable)的run方法裏,咱們先不急看run()方法,咱們回來看一下enqueueAction()方法爲何把action添加進入mPendingActions裏?由於從後面的代碼中能夠了解每次commit後都會將mPendingActions集合清空,那爲何還要使用集合保存呢?不知作別的小夥伴有沒有這方面的糾結,我一開始反正是糾結的。那咱們來分析一下使用mPendingActions的緣由:咱們知道了commit的後續操做是在mExecCommit(Runnable)的run方法裏,而mExecCommit又是經過Handler看成消息post出去的,所以這裏就能夠把commit操做看成是在異步中執行的邏輯。這又是什麼緣由呢?那是由於Handler發出去的消息並非被Looper立刻執行的,而是須要先從消息隊列中取出來再去執行,所以在這個空隙,咱們能夠會屢次切換fragment(場景:app的首頁是由底部多個Tab+多個fragmnet實現,咱們頻繁屢次切換tab)而致使屢次生成action,所以須要一個集合來看成隊列將多個action添加進去,在後面統一處理。

這裏咱們再說一下幾種提交操做的方法:

  • commit();
  • commitAllowingStateLoss();
  • commitNow();
  • commitNowAllowingStateLoss();

commit() vs commitAllowingStateLoss()

用commit()提交有時候會遇到IllegalStateException, 說你在onSaveInstanceState()以後提交, commit()和commitAllowingStateLoss()在實現上惟一的不一樣就是當你調用commit()的時候, FragmentManger會檢查是否已經存儲了它本身的狀態, 若是已經存了, 就拋出IllegalStateException。 那麼若是你調用的是commitAllowingStateLoss(),則FragmentManger不會檢查是否已經存儲了它本身的狀態(上面代碼中已添加備註說明),而且要是在onSaveInstanceState()以後,你可能會丟失掉什麼狀態呢? 答案是你可能會丟掉FragmentManager的狀態, 即save以後任何被添加或被移除的Fragments.

commit(), commitNow() 和 executePendingTransactions()

使用commit()的時候, 一旦調用, 這個commit並非當即執行的, 它會被髮送到主線程的任務隊列當中去, 當主線程準備好執行它的時候執行. popBackStack()的工做也是這樣, 發送到主線程任務隊列中去. 也即說它們都是異步的.可是有時候你但願你的操做是當即執行的,以前的開發者會在commit()調用以後加上 executePendingTransactions()來保證當即執行, 即變異步爲同步.support library從v24.0.0開始提供了 commitNow()方法,以前用executePendingTransactions()會將全部pending在隊列中還有你新提交的transactions都執行了, 而commitNow()將只會執行你當前要提交的transaction. 因此commitNow()避免你會不當心執行了那些你可能並不想執行的transactions.

可是你不能對要加在back stack中的transaction使用commitNow(),即addToBackStack()和commitNow()不能同時使用.爲何呢? 想一想一下, 若是你有一個提交使用了commit(), 緊接着又有另外一個提交使用了commitNow(), 兩個都想加入back stack, 那back stack會變成什麼樣呢? 究竟是哪一個transaction在上, 哪一個在下? 答案將是一種不肯定的狀態, 由於系統並無提供任何保證來確保順序, 因此係統決定乾脆不支持這個操做.前面提過popBackStack()是異步的, 因此它一樣也有一個同步的兄弟popBackStackImmediate().因此實際應用的時候怎麼選擇呢? 1.若是你須要同步的操做, 而且你不須要加到back stack裏, 使用commitNow(). support library在FragmentPagerAdapter裏就使用了commitNow()來保證在更新結束的時候, 正確的頁面被加上或移除. 2.若是你操做不少transactions, 而且不須要同步, 或者你須要把transactions加在back stack裏, 那就使用commit(). 3.若是你但願在某一個指定的點, 確保全部的transactions都被執行, 那麼使用executePendingTransactions().

好啦,咱們能夠繼續分析接下的啦(mExecCommit(Runnable)的run方法):

// FragmentManagerImpl類:
    Runnable mExecCommit = new Runnable() {
        public void run() {
            FragmentManagerImpl.this.execPendingActions();
        }
    };
    
    public boolean execPendingActions() {
        this.ensureExecReady(true);

        boolean didSomething;
        // mTmpRecords:臨時存儲全部待執行的動做(mPendingActions)生成的 BackStackRecord
        // mTmpIsPop:存儲 BackStackRecord 是否爲出棧。
        for(didSomething = false; this.generateOpsForPendingActions(this.mTmpRecords, this.mTmpIsPop); didSomething = true) {
            this.mExecutingActions = true;

            try {
                this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop);
            } finally {
                this.cleanupExec();
            }
        }

        this.doPendingDeferredStart();
        this.burpActive();
        return didSomething;
    }
    
    // 遍歷 mPendingActions 調用 OpGenerator.generateOps() 方法生成 BackStackRecord 添加到 mTmpRecords 並把是否爲出棧添加到 mTmpIsPop 中
    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records, ArrayList<Boolean> isPop) {
        boolean didSomething = false;
        synchronized(this) {
            if (this.mPendingActions != null && this.mPendingActions.size() != 0) {
                int numActions = this.mPendingActions.size();

                for(int i = 0; i < numActions; ++i) {
                    didSomething |= ((FragmentManagerImpl.OpGenerator)this.mPendingActions.get(i)).generateOps(records, isPop);
                }
                // 清空待定任務隊列
                this.mPendingActions.clear();
                this.mHost.getHandler().removeCallbacks(this.mExecCommit);
                return didSomething;
            } else {
                return false;
            }
        }
    }
    
// BackStackRecord類
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        if (FragmentManagerImpl.DEBUG) {
            Log.v("FragmentManager", "Run: " + this);
        }

        records.add(this);
        // 添加false
        isRecordPop.add(false);
        if (this.mAddToBackStack) {
            // 添加到「回退棧」中
            this.mManager.addBackStackState(this);
        }

        return true;
    }
    
// FragmentManagerImpl類的內部類PopBackStackState:
    private class PopBackStackState implements FragmentManagerImpl.OpGenerator {
        ...
        public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
            ...
            return FragmentManagerImpl.this.popBackStackState(records, isRecordPop, this.mName, this.mId, this.mFlags);
        }
    }
// FragmentManagerImpl類:
    boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, String name, int id, int flags) {
        if (this.mBackStack == null) {
            return false;
        } else {
            int index;
            if (name == null && id < 0 && (flags & 1) == 0) {
                ...
                records.add(this.mBackStack.remove(index));
                // 添加true
                isRecordPop.add(true);
            } else {
                ...
                for(int i = this.mBackStack.size() - 1; i > index; --i) {
                    records.add(this.mBackStack.remove(i));
                    // 添加true
                    isRecordPop.add(true);
                }
            }

            return true;
        }
    }
    
    void addBackStackState(BackStackRecord state) {
        if (this.mBackStack == null) {
            this.mBackStack = new ArrayList();
        }
        // 「回退棧」 == mBackStack(ArrayList<BackStackRecord>)
        this.mBackStack.add(state);
    }
複製代碼

在 Runnable 中執行 execPendingActions() 方法,該方法分爲幾點來分析:

經過generateOpsForPendingActions方法遍歷 mPendingActions 調用 OpGenerator.generateOps() 方法設置了 mTmpRecords(臨時存儲全部待執行的動做:BackStackRecord) 和 mTmpIsPop (存儲 BackStackRecord 是否爲出棧) OpGenerator.generateOps()方法,上面曾提起過OpGenerator接口會有兩個地方實現,而BackStackRecord類實現OpGenerator接口中的generateOps()方法上面代碼給出了源碼,其實就是設置this自身添加到records(即mTmpRecords)集合中,並同時添加一個false到isRecordPop(即mTmpIsPop)集合裏表示此動做不是「回退棧」的出棧操做。而另外一個實現OpGenerator接口的generateOps()方法裏isRecordPop(即mTmpIsPop)集合內添加的是true(源碼已展現)

咱們接着往下走removeRedundantOperationsAndExecute():

// FragmentManagerImpl類:
    private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        if (records != null && !records.isEmpty()) {
            if (isRecordPop != null && records.size() == isRecordPop.size()) {
                this.executePostponedTransaction(records, isRecordPop);
                int numRecords = records.size();
                int startIndex = 0;

                for(int recordNum = 0; recordNum < numRecords; ++recordNum) {
                    boolean canReorder = ((BackStackRecord)records.get(recordNum)).mReorderingAllowed;
                    if (!canReorder) {
                        if (startIndex != recordNum) {
                            this.executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                        }

                        int reorderingEnd = recordNum + 1;
                        // 根據上面的分析,只有「回退棧」執行出棧纔會執行此處代碼
                        if ((Boolean)isRecordPop.get(recordNum)) {
                            while(reorderingEnd < numRecords && (Boolean)isRecordPop.get(reorderingEnd) && !((BackStackRecord)records.get(reorderingEnd)).mReorderingAllowed) {
                                ++reorderingEnd;
                            }
                        }

                        this.executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
                        startIndex = reorderingEnd;
                        recordNum = reorderingEnd - 1;
                    }
                }

                if (startIndex != numRecords) {
                    this.executeOpsTogether(records, isRecordPop, startIndex, numRecords);
                }

            } else {
                throw new IllegalStateException("Internal error with the back stack records");
            }
        }
    }
    
    private void executeOpsTogether(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
        boolean allowReordering = ((BackStackRecord)records.get(startIndex)).mReorderingAllowed;
        boolean addToBackStack = false;
        if (this.mTmpAddedFragments == null) {
            this.mTmpAddedFragments = new ArrayList();
        } else {
            this.mTmpAddedFragments.clear();
        }

        this.mTmpAddedFragments.addAll(this.mAdded);
        Fragment oldPrimaryNav = this.getPrimaryNavigationFragment();

        int postponeIndex;
        for(postponeIndex = startIndex; postponeIndex < endIndex; ++postponeIndex) {
            BackStackRecord record = (BackStackRecord)records.get(postponeIndex);
            boolean isPop = (Boolean)isRecordPop.get(postponeIndex);
            // 對mOps進行優化,add或者remove(mOps即第三部分提到的保存add進的fragmnet集合)
            if (!isPop) {
                // 在28以前這裏會執行 expandReplaceOps 方法把 replace 替換(目標 fragment 已經被 add )成相應的 remove 和 add 兩個操做,或者(目標 fragment 沒有被 add )只替換成 add 操做。
                oldPrimaryNav = record.expandOps(this.mTmpAddedFragments, oldPrimaryNav);
            } else {
                oldPrimaryNav = record.trackAddedFragmentsInPop(this.mTmpAddedFragments, oldPrimaryNav);
            }

            addToBackStack = addToBackStack || record.mAddToBackStack;
        }

        this.mTmpAddedFragments.clear();
        if (!allowReordering) {
            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex, false);
        }
        // 若是allowReordering爲true,則此方法後面會走不通,則會走下面的判斷,最終都會到達咱們這部分最重要的方法————moveToState
        executeOps(records, isRecordPop, startIndex, endIndex);
        postponeIndex = endIndex;
        if (allowReordering) {
        /**
         * allowReordering爲true,走此邏輯
         *  容許從新排序(須要本身調用FragmentTransaction.setReorderingAllowed()方法設置)
         *  必須啓用fragment事務中的從新排序(即allowReordering),才能
         *  使延遲的fragment過渡生效,具體用法請參考:https://www.jianshu.com/p/232073710172
        */
            ArraySet<Fragment> addedFragments = new ArraySet();
            this.addAddedFragments(addedFragments);
            // 此方法最後會走到moveToState方法,具體源碼請自行查看
            postponeIndex = this.postponePostponableTransactions(records, isRecordPop, startIndex, endIndex, addedFragments);
            this.makeRemovedFragmentsInvisible(addedFragments);
        }
        ...
    }
    
    private static void executeOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
        for(int i = startIndex; i < endIndex; ++i) {
            BackStackRecord record = (BackStackRecord)records.get(i);
            boolean isPop = (Boolean)isRecordPop.get(i);
            // 從上面分析可知isRecordPop取出來的是false(「回退棧」出棧時爲trueif (isPop) {
                record.bumpBackStackNesting(-1);
                boolean moveToState = i == endIndex - 1;
                // 若爲回退棧出棧操做,則執行此方法,
                // 此方法中根據op.cmd判斷對framgnet進行相應的處理,
                // 與else分支相同的cmd指令處理邏輯不一樣
                record.executePopOps(moveToState);
            } else {
                record.bumpBackStackNesting(1);
                record.executeOps();
            }
        }
    }
複製代碼

咱們從removeRedundantOperationsAndExecute()方法的源碼中能夠看到該方法其實是對records集合中全部動做的startIndex(起始動做位置), recordNum(須要操做的動做個數)的設置,而後都會去調用executeOpsTogether()方法,而executeOpsTogether()方法咱們只展現部分代碼,其中會對mOps進行擴展操做,最後調用方法executeOps()繼續操做,而方法executeOps中又通過判斷最終調用BackStackRecord類的executeOps()方法。

// BackStackRecord類:
    void executeOps() {
        int numOps = this.mOps.size();
        // 遍歷執行全部的mOps(包含咱們commit操做前的全部其它操做,好比:add、hide等)
        for(int opNum = 0; opNum < numOps; ++opNum) {
            BackStackRecord.Op op = (BackStackRecord.Op)this.mOps.get(opNum);
            Fragment f = op.fragment;
            if (f != null) {
                f.setNextTransition(this.mTransition, this.mTransitionStyle);
            }
            // 根據op.cmd指令進行操做,相信根據下面每一個分支的邏輯能分別出每一個指令對應的操做
            switch(op.cmd) {
            case 1:
                f.setNextAnim(op.enterAnim);
                this.mManager.addFragment(f, false);
                break;
            case 2:
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            case 3:
                f.setNextAnim(op.exitAnim);
                this.mManager.removeFragment(f);
                break;
            case 4:
                f.setNextAnim(op.exitAnim);
                this.mManager.hideFragment(f);
                break;
            case 5:
                f.setNextAnim(op.enterAnim);
                this.mManager.showFragment(f);
                break;
            case 6:
                f.setNextAnim(op.exitAnim);
                this.mManager.detachFragment(f);
                break;
            case 7:
                f.setNextAnim(op.enterAnim);
                this.mManager.attachFragment(f);
                break;
            case 8:
                this.mManager.setPrimaryNavigationFragment(f);
                break;
            case 9:
                this.mManager.setPrimaryNavigationFragment((Fragment)null);
            }

            if (!this.mReorderingAllowed && op.cmd != 1 && f != null) {
                this.mManager.moveFragmentToExpectedState(f);
            }
        }
        // 只有沒設置setReorderingAllowed(true)的才能繼續,
        // 而設置的會在前面的某步邏輯當中走到moveToState方法內,上面有說明
        if (!this.mReorderingAllowed) {
            // 最後調用咱們這部分最重要的方法:moveToState
            this.mManager.moveToState(this.mManager.mCurState, true);
        }
    }
 
 // 1. 提交add操做時將當前提交的fragmen添加進mActive和mAdded裏
    // 並重置fragment.mAdded和fragment.mRemoving兩個的狀態
    public void addFragment(Fragment fragment, boolean moveToStateNow) {
        if (DEBUG) {
            Log.v("FragmentManager", "add: " + fragment);
        }
        // 調用makeActive,將fragment添加進mActive
        this.makeActive(fragment);
        if (!fragment.mDetached) {
            if (this.mAdded.contains(fragment)) {
                throw new IllegalStateException("Fragment already added: " + fragment);
            }

            synchronized(this.mAdded) {
                // 將fragment也添加進mAdded裏
                this.mAdded.add(fragment);
            }
            // 設置mAdded和mRemoving狀態
            fragment.mAdded = true;
            fragment.mRemoving = false;
            if (fragment.mView == null) {
                fragment.mHiddenChanged = false;
            }

            if (fragment.mHasMenu && fragment.mMenuVisible) {
                this.mNeedMenuInvalidate = true;
            }

            if (moveToStateNow) {
                this.moveToState(fragment);
            }
        }
    }
    void makeActive(Fragment f) {
        if (f.mIndex < 0) {
            f.setIndex(this.mNextFragmentIndex++, this.mParent);
            if (this.mActive == null) {
                this.mActive = new SparseArray();
            }
            // 將fragment添加進mActive
            this.mActive.put(f.mIndex, f);
            if (DEBUG) {
                Log.v("FragmentManager", "Allocated fragment index " + f);
            }
        }
    }

// 2. 提交remove操做時將當前提交的fragment從mAdded移除
    // 並重置fragment.mAdded和fragment.mRemoving兩個的狀態
    public void removeFragment(Fragment fragment) {
        if (DEBUG) {
            Log.v("FragmentManager", "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        }

        boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            synchronized(this.mAdded) {
                this.mAdded.remove(fragment);
            }

            if (fragment.mHasMenu && fragment.mMenuVisible) {
                this.mNeedMenuInvalidate = true;
            }

            fragment.mAdded = false;
            fragment.mRemoving = true;
        }

    }
    
// 3. 設置fragment.mHidden的狀態爲false
    public void showFragment(Fragment fragment) {
        if (DEBUG) {
            Log.v("FragmentManager", "show: " + fragment);
        }

        if (fragment.mHidden) {
            fragment.mHidden = false;
            fragment.mHiddenChanged = !fragment.mHiddenChanged;
        }

    }
    
// 4. 設置fragment.mHidden的狀態爲true
    public void hideFragment(Fragment fragment) {
        if (DEBUG) {
            Log.v("FragmentManager", "hide: " + fragment);
        }

        if (!fragment.mHidden) {
            fragment.mHidden = true;
            fragment.mHiddenChanged = !fragment.mHiddenChanged;
        }

    }
複製代碼

咱們從executeOps()方法中能夠看到該方法裏經過for循環對mOps進行了遍歷,而這次遍歷會對咱們本次commit提交的全部操做進行設置。好比咱們上面列出的四中操做:add、remove、show和hide同樣。

咱們先來看這段代碼中的兩個集合:mAdded和mActive

  • mAdded:包含了全部已經 added 而且沒有被從Activity中removed和detached的Fragments 注:若是一個 Fragment被添加到Activity中那麼這個Fragment會被added到該列表。Fragment被從Activity中removed或者Fragment從Activity中detached,則就會被從該列表中移除。

  • mAdded 的一個超集,是綁定到一個 Activity 上的全部 Fragment。包括返回棧中全部的經過任何 FragmentTransaction 添加的 Fragments。這是很是重要的由於以下緣由:

    • 當一個 Activity 要保存它的 State 時,它必須保存它全部 Fragment 的狀態,由於 mActive 保存了全部 Fragment,因此係統只要存儲這個列表裏的 Fragment 的狀態就行了。而mAdded 只是被序列化成一個整形數組,每一個元素指向 Fragment 在 mActive 中的下標位置(這塊在前面 Fragment 的存儲與恢復中分析到了)。

    • 在恢復 Activity 的狀態時,FragmentManager 的狀態也會被恢復,mActive 列表就能夠被用來恢復 mAdded 列表,由於保存狀態的時候mAdded 被簡單的保存爲整形數組。

    • 當一個 Activity 經歷它的各生命週期時,它必須引發全部綁定的 Fragment 經歷各自的生命週期。

      該 Activity 的 FragmentManager 有義務去引導全部 Fragemnt 轉換到正確的狀態,這其中包括屏幕上可見的 Fragment 的 View 層級的初始化,而且調用正確的生命週期函數。

      爲了確保完整,FragmentManager 將遍歷mActive 中全部的 Fragment,而不只僅是 mAdded。

    • 它持有全部 BackStack 返回棧引用的對象。

      這確保了返回棧中對 Fragment 操做的回滾可以實現。

注:若是一個Fragment被添加到Activity中那麼這個Fragment會被added到該列表。只有在兩種狀況 Fragment纔會被從該列表中移除:一是,Fragment被從Activity中移除而且沒有在返回棧中;二是一個transaction從返回棧中被pop出來、Fragment的add或者replace操做被逆向,即返回棧再也不持有 Fragment。

咱們接着來講moveToState這個方法:

// FragmentManagerImpl類:
    void moveToState(int newState, boolean always) {
        if (this.mHost == null && newState != 0) {
            throw new IllegalStateException("No activity");
        } else if (always || newState != this.mCurState) {
            this.mCurState = newState;
            if (this.mActive != null) {
                int numAdded = this.mAdded.size();
                
                int numActive;
                // 遍歷mAdded集合,確定會走此代碼邏輯
                for(numActive = 0; numActive < numAdded; ++numActive) {
                    Fragment f = (Fragment)this.mAdded.get(numActive);
                    // 將fragment移至預期狀態
                    this.moveFragmentToExpectedState(f);
                }

                numActive = this.mActive.size();
                // // 遍歷mActive集合(若調用回退棧出棧,則會走此出代碼)
                for(int i = 0; i < numActive; ++i) {
                    Fragment f = (Fragment)this.mActive.valueAt(i);
                    // 當前framgnet不爲空 而且 此時操做爲remove或者detach 而且不是新添加的則會執行下面代碼
                    if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
                        // // 將fragment移至預期狀態
                        this.moveFragmentToExpectedState(f);
                    }
                }
                // fragment的成員屬性f.mDeferStart爲true才能走通下面代碼(具體暫不分析)
                this.startPendingDeferredFragments();
                if (this.mNeedMenuInvalidate && this.mHost != null && this.mCurState == 4) {
                    this.mHost.onSupportInvalidateOptionsMenu();
                    this.mNeedMenuInvalidate = false;
                }
            }
        }
    }
複製代碼

咱們來看一下moveToState方法中的參數newState,BackStackRecord類中的executeOps裏傳過來的是mManager.mCurState,而mManager.mCurState默認爲0,即須要add的狀態。咱們如今回想一下fragment爲何會隨着Activity的週期變化而變化呢?咱們來看段代碼:

// FragmentActivity類:
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        this.mFragments.attachHost((Fragment)null);
        super.onCreate(savedInstanceState);
        ......
        this.mFragments.dispatchCreate();
    }

// FragmentController類:
    public void dispatchCreate() {
        this.mHost.mFragmentManager.dispatchCreate();
    }
    
// FragmentManagerImpl類:
    public void dispatchCreate() {
        this.mStateSaved = false;
        this.mStopped = false;
        this.dispatchStateChange(1);
    }
    private void dispatchStateChange(int nextState) {
        try {
            this.mExecutingActions = true;
            // 這裏走到了moveToState
            this.moveToState(nextState, false);
        } finally {
            this.mExecutingActions = false;
        }

        this.execPendingActions();
    }
複製代碼

這段代碼是從上到下的順序執行的,咱們能夠看到在FragmentActivity的onCreate週期方法中一步一步的走到了moveToState,而moveToState正是走到fragment週期方法的關鍵(FragmentActivity的其它週期方法同onCreate方法也會走到對應的fragment的週期方法中)。既然moveToState是走到fragment週期方法的關鍵,那咱們繼續往下分析,上面已經說到會走到FragmentManagerImpl類的moveFragmentToExpectedState(f)方法中:

// FragmentManagerImpl類:
    void moveFragmentToExpectedState(Fragment f) {
        if (f != null) {
            int nextState = this.mCurState;
            if (f.mRemoving) {
                // 若是操做爲remove則nextState設置爲1或者0,用於後面判斷
                if (f.isInBackStack()) {
                    nextState = Math.min(nextState, 1);
                } else {
                    nextState = Math.min(nextState, 0);
                }
            }
            // 繼續走同名方法
            this.moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
            // 若是當前View不爲空,則添加布局執行動畫
            if (f.mView != null) {
                Fragment underFragment = this.findFragmentUnder(f);
                if (underFragment != null) {
                    View underView = underFragment.mView;
                    ViewGroup container = f.mContainer;
                    int underIndex = container.indexOfChild(underView);
                    int viewIndex = container.indexOfChild(f.mView);
                    if (viewIndex < underIndex) {
                        container.removeViewAt(viewIndex);
                        container.addView(f.mView, underIndex);
                    }
                }

                if (f.mIsNewlyAdded && f.mContainer != null) {
                    if (f.mPostponedAlpha > 0.0F) {
                        f.mView.setAlpha(f.mPostponedAlpha);
                    }

                    f.mPostponedAlpha = 0.0F;
                    f.mIsNewlyAdded = false;
                    FragmentManagerImpl.AnimationOrAnimator anim = this.loadAnimation(f, f.getNextTransition(), true, f.getNextTransitionStyle());
                    if (anim != null) {
                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
                        if (anim.animation != null) {
                            f.mView.startAnimation(anim.animation);
                        } else {
                            anim.animator.setTarget(f.mView);
                            anim.animator.start();
                        }
                    }
                }
            }

            if (f.mHiddenChanged) {
                // 完成顯示隱藏fragment
                this.completeShowHideFragment(f);
            }

        }
    }
    
    void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
        // 從新判斷設置newState狀態值
        if ((!f.mAdded || f.mDetached) && newState > 1) {
            newState = 1;
        }

        if (f.mRemoving && newState > f.mState) {
            if (f.mState == 0 && f.isInBackStack()) {
                newState = 1;
            } else {
                newState = f.mState;
            }
        }

        if (f.mDeferStart && f.mState < 3 && newState > 2) {
            newState = 2;
        }
        
        // 若是fragment自身的狀態<=newState狀態,則證實此時fragment是被建立階段
        if (f.mState <= newState) {
            label297: {
                if (f.mFromLayout && !f.mInLayout) {
                    return;
                }

                if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                    f.setAnimatingAway((View)null);
                    f.setAnimator((Animator)null);
                    this.moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
                }
                // 判斷fragment狀態進行處理
                switch(f.mState) {
                case 0:
                    if (newState > 0) {
                        if (DEBUG) {
                            Log.v("FragmentManager", "moveto CREATED: " + f);
                        }

                        if (f.mSavedFragmentState != null) {
                            f.mSavedFragmentState.setClassLoader(this.mHost.getContext().getClassLoader());
                            f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray("android:view_state");
                            f.mTarget = this.getFragment(f.mSavedFragmentState, "android:target_state");
                            if (f.mTarget != null) {
                                f.mTargetRequestCode = f.mSavedFragmentState.getInt("android:target_req_state", 0);
                            }

                            if (f.mSavedUserVisibleHint != null) {
                                f.mUserVisibleHint = f.mSavedUserVisibleHint;
                                f.mSavedUserVisibleHint = null;
                            } else {
                                f.mUserVisibleHint = f.mSavedFragmentState.getBoolean("android:user_visible_hint", true);
                            }

                            if (!f.mUserVisibleHint) {
                                f.mDeferStart = true;
                                if (newState > 2) {
                                    newState = 2;
                                }
                            }
                        }

                        f.mHost = this.mHost;
                        f.mParentFragment = this.mParent;
                        f.mFragmentManager = this.mParent != null ? this.mParent.mChildFragmentManager : this.mHost.getFragmentManagerImpl();
                        if (f.mTarget != null) {
                            if (this.mActive.get(f.mTarget.mIndex) != f.mTarget) {
                                throw new IllegalStateException("Fragment " + f + " declared target fragment " + f.mTarget + " that does not belong to this FragmentManager!");
                            }

                            if (f.mTarget.mState < 1) {
                                this.moveToState(f.mTarget, 1, 0, 0, true);
                            }
                        }

                        this.dispatchOnFragmentPreAttached(f, this.mHost.getContext(), false);
                        f.mCalled = false;
                        // 執行fragment的onAttach週期方法
                        f.onAttach(this.mHost.getContext());
                        if (!f.mCalled) {
                            throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onAttach()");
                        }

                        if (f.mParentFragment == null) {
                            this.mHost.onAttachFragment(f);
                        } else {
                            f.mParentFragment.onAttachFragment(f);
                        }

                        this.dispatchOnFragmentAttached(f, this.mHost.getContext(), false);
                        if (!f.mIsCreated) {
                            this.dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
                            f.performCreate(f.mSavedFragmentState);
                            this.dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                        } else {
                            f.restoreChildFragmentState(f.mSavedFragmentState);
                            f.mState = 1;
                        }

                        f.mRetaining = false;
                    }
                case 1:
                    this.ensureInflatedFragmentView(f);
                    if (newState > 1) {
                        if (DEBUG) {
                            Log.v("FragmentManager", "moveto ACTIVITY_CREATED: " + f);
                        }

                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                if (f.mContainerId == -1) {
                                    this.throwException(new IllegalArgumentException("Cannot create fragment " + f + " for a container view with no id"));
                                }

                                container = (ViewGroup)this.mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    String resName;
                                    try {
                                        resName = f.getResources().getResourceName(f.mContainerId);
                                    } catch (NotFoundException var9) {
                                        resName = "unknown";
                                    }

                                    this.throwException(new IllegalArgumentException("No view found for id 0x" + Integer.toHexString(f.mContainerId) + " (" + resName + ") for fragment " + f));
                                }
                            }

                            f.mContainer = container;
                           // 執行fragment的onCreateView週期方法 f.performCreateView(f.performGetLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView == null) {
                                f.mInnerView = null;
                            } else {
                                f.mInnerView = f.mView;
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                                    container.addView(f.mView);
                                }

                                if (f.mHidden) {
                                    f.mView.setVisibility(8);
                                }
                                // 執行fragment的onViewCreated週期方法
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                                this.dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
                                f.mIsNewlyAdded = f.mView.getVisibility() == 0 && f.mContainer != null;
                            }
                        }

                        f.performActivityCreated(f.mSavedFragmentState);
                        this.dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                        if (f.mView != null) {
                            f.restoreViewState(f.mSavedFragmentState);
                        }
                        // 執行fragment的onActivityCreated週期方法
                        f.mSavedFragmentState = null;
                    }
                case 2:
                    if (newState > 2) {
                        if (DEBUG) {
                            Log.v("FragmentManager", "moveto STARTED: " + f);
                        }
                        // 執行framgnet的onStart週期方法
                        f.performStart();
                        this.dispatchOnFragmentStarted(f, false);
                    }
                case 3:
                    break;
                default:
                    break label297;
                }

                if (newState > 3) {
                    if (DEBUG) {
                        Log.v("FragmentManager", "moveto RESUMED: " + f);
                    }
                    // 執行framgnet的onResume週期方法
                    f.performResume();
                    this.dispatchOnFragmentResumed(f, false);
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
            }
        // 若是fragment自身的狀態>=newState狀態,則證實此時fragment是被銷燬階段
        } else if (f.mState > newState) {
            switch(f.mState) {
            case 4:
                if (newState < 4) {
                    if (DEBUG) {
                        Log.v("FragmentManager", "movefrom RESUMED: " + f);
                    }
                    // 執行framgnet的onPause週期方法
                    f.performPause();
                    this.dispatchOnFragmentPaused(f, false);
                }
            case 3:
                if (newState < 3) {
                    if (DEBUG) {
                        Log.v("FragmentManager", "movefrom STARTED: " + f);
                    }
                    // 執行framgnet的onStop週期方法
                    f.performStop();
                    this.dispatchOnFragmentStopped(f, false);
                }
            case 2:
                if (newState < 2) {
                    if (DEBUG) {
                        Log.v("FragmentManager", "movefrom ACTIVITY_CREATED: " + f);
                    }

                    if (f.mView != null && this.mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
                        this.saveFragmentViewState(f);
                    }
                    // 執行framgnet的onDestroyView週期方法
                    f.performDestroyView();
                    this.dispatchOnFragmentViewDestroyed(f, false);
                    if (f.mView != null && f.mContainer != null) {
                        f.mContainer.endViewTransition(f.mView);
                        f.mView.clearAnimation();
                        FragmentManagerImpl.AnimationOrAnimator anim = null;
                        if (this.mCurState > 0 && !this.mDestroyed && f.mView.getVisibility() == 0 && f.mPostponedAlpha >= 0.0F) {
                            anim = this.loadAnimation(f, transit, false, transitionStyle);
                        }

                        f.mPostponedAlpha = 0.0F;
                        if (anim != null) {
                            this.animateRemoveFragment(f, anim, newState);
                        }

                        f.mContainer.removeView(f.mView);
                    }

                    f.mContainer = null;
                    f.mView = null;
                    f.mViewLifecycleOwner = null;
                    f.mViewLifecycleOwnerLiveData.setValue((Object)null);
                    f.mInnerView = null;
                    f.mInLayout = false;
                }
            case 1:
                if (newState < 1) {
                    if (this.mDestroyed) {
                        if (f.getAnimatingAway() != null) {
                            View v = f.getAnimatingAway();
                            f.setAnimatingAway((View)null);
                            v.clearAnimation();
                        } else if (f.getAnimator() != null) {
                            Animator animator = f.getAnimator();
                            f.setAnimator((Animator)null);
                            animator.cancel();
                        }
                    }

                    if (f.getAnimatingAway() == null && f.getAnimator() == null) {
                        if (DEBUG) {
                            Log.v("FragmentManager", "movefrom CREATED: " + f);
                        }

                        if (!f.mRetaining) {
                            // 執行framgnet的onDestroy週期方法
                            f.performDestroy();
                            this.dispatchOnFragmentDestroyed(f, false);
                        } else {
                            f.mState = 0;
                        }
                        // 執行framgnet的onDetach週期方法
                        f.performDetach();
                        this.dispatchOnFragmentDetached(f, false);
                        if (!keepActive) {
                            if (!f.mRetaining) {
                                this.makeInactive(f);
                            } else {
                                f.mHost = null;
                                f.mParentFragment = null;
                                f.mFragmentManager = null;
                            }
                        }
                    } else {
                        f.setStateAfterAnimating(newState);
                        newState = 1;
                    }
                }
            }
        }

        if (f.mState != newState) {
            Log.w("FragmentManager", "moveToState: Fragment state for " + f + " not updated inline; " + "expected state " + newState + " found " + f.mState);
            f.mState = newState;
        }

    }
複製代碼

繼續從FragmentManagerImpl類的moveFragmentToExpectedState(f)方法中提及,該方法中又會繼續調用moveToState方法,這個方法和上面的moveToState方法不一樣,這倆方法是同名不一樣參的方法,該方法中會根據fragment的mState自身的狀態值和newState傳過來的狀態值進行比較來區分:當前fragment是走建立階段的週期方法仍是銷燬階段的週期方法,進一步再經過fragment的mState判斷到底要走哪一個fragment的週期方法,具體標註可看代碼註釋哦。

Fragment的7種狀態(mState):

static final int INVALID_STATE = -1; // 做爲null值的非法狀態

static final int INITIALIZING = 0; // 沒有被create

static final int CREATED = 1; // 已經create

static final int ACTIVITY_CREATED = 2; // Activity已經完成了create

static final int STOPPED = 3; // 徹底建立,還沒start

static final int STARTED = 4; // 已經create和start,尚未resume

static final int RESUMED = 5; // 已經完成create,start和resume

「回退棧」:BackStackRecord 出棧

咱們最後說一下回退棧(FragmentManagerImpl的成員mBackStack),其實咱們在cooimt操做時咱們就已經設置了「回退棧」內的元素。從新看一下該部分代碼:

// FragmentManagerImpl類:
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        if (FragmentManagerImpl.DEBUG) {
            Log.v("FragmentManager", "Run: " + this);
        }

        records.add(this);
        isRecordPop.add(false);
        if (this.mAddToBackStack) {
            this.mManager.addBackStackState(this);
        }

        return true;
    }
    void addBackStackState(BackStackRecord state) {
        if (this.mBackStack == null) {
            this.mBackStack = new ArrayList();
        }

        this.mBackStack.add(state);
    }
複製代碼

咱們回顧一下上面的邏輯,在執行BackStackRecord類(實現了OpGenerator接口)的方法generateOps時,就已經將當前的BackStackRecord入棧啦。而BackStackRecord出棧主要是調用以下幾個方法:

  • popBackStack()
  • popBackStackImmediate()
  • popBackStack(int id/String name, int flags)
  • popBackStackImmediate(int id/String name, int flags)

PopBackStackState類 實現了 OpGenerator 接口,具體實現以下:

  • 參數 records 用來存放出棧的 BackStackRecord
  • 參數 isRecordPop 用來存放相應 BackStackRecord 是否爲出棧(顯然爲 true)
  • 參數 name 表示出棧到相應 name 的 BackStackRecord
  • 參數 id 表示出棧到相應 id 的 BackStackRecord
  • 參數 flags (0 或者 POP_BACK_STACK_INCLUSIVE) POP_BACK_STACK_INCLUSIVE 若是參數 flags ==POP_BACK_STACK_INCLUSIVE 而且設置了 name 或者 id 那麼,全部符合該 name 或者 id 的 BackStackRecord 都將被匹配,直到遇到一個不匹配的或者到達了棧底,而後出棧全部 BackStackRecord 直到最終匹配到的下標位置。不然只匹配第一次 name 或者 id 相符的 BackStackRecord,而後出棧全部 BackStackRecord 直到但不包括匹配到的下標位置。

若咱們本身主動調用popBackStack兩個方法之一,實際上就是調用了enqueueAction方法,並傳入PopBackStackState類的新建立實例,而此時isRecordPop集合裏存的值就是true。在上面部分分析中也說明了isRecordPop集合中存的元素對代碼邏輯的影響。最後會致使界面顯示的是上一個fragment視圖。

若是 回退棧 mBackStack 爲空就終止出棧操做並返回 false,當name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0 (調用的是popBackStack()方法)時,把返回棧最後一個 BackStackRecord出棧。當 name 或者 id 被指定的時候,倒序遍歷 mBackStack ,若是遇到 name 或者 id 相符就退出循環,此時 index 爲第一次匹配到的下標,若是flags==POP_BACK_STACK_INCLUSIVE 繼續遍歷返回棧,直至棧底或者遇到不匹配的跳出循環。最後出棧全部 BackStackRecord。

總結

  • show/hideFragment只是改變fragment根View的visibility,最多帶上個動畫效果,另外只有自己是hidden的fragment,調用show才起做用,不然沒用的,fragment.onHiddenChanged會被觸發;其次不會有生命週期callback觸發,固然了這些操做的前提是已經被add了的fragment;
  • add Fragment的時候,無論加不加入回退棧都同樣,經歷的生命週期以下:onAttach、onCreate、onCreateView、onActivityCreate、onStart、onResume;
  • removeFragment的時候,經歷的生命週期以下:onPause、onStop、onDestroyView,若是不加回退棧還會繼續走onDestroy、onDetach;remove的時候不只從mAdded中移除fragment,也從mActive中移除了
  • attach/detach Fragment的前提都是已經add了的fragment,其生命週期回調不受回退棧影響。attach的時候onCreateView、onActivityCreate、onStart、onResume會被調用;detach的時候onPause、onStop、onDestroyView會被調用,onDestroy、onDetach不會被調用;對應的fragment只是從mAdded中移除了;

Fragment State Loss

Fragment過渡動畫

相關文章
相關標籤/搜索