Android-Fragment簡要分析

注意:文章對Fragment源碼的分析基于support v4的Fragment包,版本號為25.3.1

Fragment相關類UML圖

1008428-b84d45b1ba19d73d.jpg

support包中對負責管理Fragment生命是FragmentActivity,v7包的AppCompatActivity也是繼承于它。
FragmentActivity管理Fragment是通過它內(nèi)部mFragments的變量,mFragments類型為FragmentController.

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

FragmentActivity傳遞自身生命狀態(tài)、狀態(tài)保存、恢復都是通過調(diào)用mFragments相應的方法進行處理;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);
 
    super.onCreate(savedInstanceState);
 
    //...
    mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
 
    //...
    mFragments.dispatchCreate();
}
 
public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
}

而FragmentController本身只是簡單的將相應請求轉(zhuǎn)發(fā)給FragmentManagerImpl,只是起到了橋梁溝通作用。

FragmentActivity創(chuàng)建mFragments時傳遞了一個HostCallbacks內(nèi)部類實例,這個callback對象給Fragment提供了獲取Context,啟動Activity的能力。

Fragment

Fragment主要是由Attr、View、State構(gòu)成,
Attr:固有屬性,F(xiàn)ragment基本信息,基本不會隨生命周期變化。

Bundle mArguments;  //構(gòu)造參數(shù)
boolean mFromLayout; //是否從layout文件中創(chuàng)建
String mTag;
...

View:Fragment管理View所依賴的相關成員變量。

// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
 
// The View generated for this fragment.
View mView;
 
// The real inner view that will save/restore state.
View mInnerView;

State:Fragment的狀態(tài),管理Fragment的顯示、生命周期。

int mState = INITIALIZING; //生命周期狀態(tài)
boolean mAdded; //是否被添加
boolean mRemoving; //是否被移除
boolean mHidden;// 是否被隱藏
boolean mDetached; //是否已經(jīng)分離
 
// mState取值如下
static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

Fragment的生命周期方法,onAttach, onCreate, onCreateView...都是在mState狀態(tài)變化中進行回調(diào)的。

Fragment的事務:FragmentTransaction、BackStackRecord

添加一個Fragment,我們一般使用如下代碼:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.contaniner, fragment);
transaction.commit();

FragmentTransaction表示一個事務,提供了如add、remove、attach、detach、show、hide、replace等接口操控Fragment。它的具體實現(xiàn)類是BackStackRecord,它的接口實現(xiàn)如下:

@Override
public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}
 
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    ...
    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}
 
void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}
 
@Override
public FragmentTransaction remove(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_REMOVE;
    op.fragment = fragment;
    addOp(op);
    return this;
}
...

可以看到我們調(diào)用的add、remove等方法最后都是生成一個Op對象保存在mOps列表中,Op是Operator簡寫表示操作,Op的cmd屬性則區(qū)分操作的類型,cmd的值取如下幾種:

static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
static final int OP_DETACH = 6;
static final int OP_ATTACH = 7;

上面操作完畢后,最后是commit事務了

@Override
public int commit() {
    return commitInternal(false);
}
 
@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}
 
@Override
public void commitNow() {
    ...
    // 直接開始執(zhí)行事務
    mManager.execSingleAction(this, false);
}
 
@Override
public void commitNowAllowingStateLoss() {
    ...
    // 直接開始執(zhí)行事務
    mManager.execSingleAction(this, true);
}
 
int commitInternal(boolean allowStateLoss) {
    ...
    // 事務入對象,等待下一個Handler回調(diào)執(zhí)行
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

*AllowingStateLoss表示是否允許狀態(tài)丟失,如果不允許但是當前Activity狀態(tài)已經(jīng)觸發(fā)保存了調(diào)用會拋出異常;commit、commitNow的區(qū)別則是提交事務等待下個主線程Handler回調(diào)執(zhí)行還是立即執(zhí)行,除了執(zhí)行時機不一樣外,兩者最后觸發(fā)的事務執(zhí)行邏輯是一樣的。

事務執(zhí)行邏輯依然在BackStackRecord中:

void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        f.setNextTransition(mTransition, mTransitionStyle);
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        if (!mAllowOptimization && op.cmd != OP_ADD) {
            mManager.moveFragmentToExpectedState(f);
        }
    }
    if (!mAllowOptimization) {
        // Added fragments are added at the end to comply with prior behavior.
        mManager.moveToState(mManager.mCurState, true);
    }
}

執(zhí)行邏輯是遍歷所有添加的操作,執(zhí)行相應的FragmentManage方法,最后將Fragment的狀態(tài)移動的相應的狀態(tài)。

Fragment狀態(tài)變遷:moveToState

Fragment添加到FragmentMange中后,要將它移動到相應的狀態(tài),使Fragment正確的顯示、交互。

FragmentManage的成員變量mCurState存儲著當前Activity狀態(tài)對應的Fragment狀態(tài)值,它的取值范圍如同F(xiàn)ragment。Activity的狀態(tài)發(fā)生變化時都會觸發(fā)相應方法修改mCurState,并將所有的active 的Fragment移動到相應的狀態(tài)上。

public void dispatchCreate() {
    mStateSaved = false;
    moveToState(Fragment.CREATED, false);
}
 
public void dispatchActivityCreated() {
    mStateSaved = false;
    moveToState(Fragment.ACTIVITY_CREATED, false);
}
 
public void dispatchStart() {
    mStateSaved = false;
    moveToState(Fragment.STARTED, false);
}
 
public void dispatchResume() {
    mStateSaved = false;
    moveToState(Fragment.RESUMED, false);
}
 
public void dispatchPause() {
    moveToState(Fragment.STARTED, false);
}
 
public void dispatchStop() {
    // See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    mStateSaved = true;
 
    moveToState(Fragment.STOPPED, false);
}
 
public void dispatchReallyStop() {
    moveToState(Fragment.ACTIVITY_CREATED, false);
}
 
public void dispatchDestroyView() {
    moveToState(Fragment.CREATED, false);
}
 
public void dispatchDestroy() {
    mDestroyed = true;
    execPendingActions();
    moveToState(Fragment.INITIALIZING, false);
    mHost = null;
    mContainer = null;
    mParent = null;
}

FragmentManage的moveToState方法就負責了將自己管理的所有Fragment的狀態(tài)移動到對應的狀態(tài)上,當mCurState變化,或者Fragment操作觸發(fā)都會調(diào)用該方法。

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    // Fragments that are not currently added will sit in the onCreate() state.
    ...
    if (f.mState < newState) {
        // For fragments that are created from a layout, when restoring from
        // state we don't want to allow them to be created until they are
        // being reloaded from the layout.
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                   ...
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                    f.performPause();
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                    f.performStop();
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                   ...
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    ...
                }
        }
    }
 
    ...
}

fragment的state取值,為前面提到的七中狀態(tài),其中最低值是INITIALIZING狀態(tài),代表fragment剛創(chuàng)建,還未被add, 最高狀態(tài)值是RESUMED,代表fragment處于前臺。 所以moveToState內(nèi)部分兩條線,狀態(tài)躍升,和狀態(tài)降低,里面各有一個switch判斷,注意到switch里每個case都沒有break,這意味著,狀態(tài)可以持續(xù)變遷,比如從INITIALIZING,一直躍升到RESUMED,將每個case都走一遍,每次case語句內(nèi),都會改變state的值。

Untitled.png

Fragment狀態(tài)保存、恢復

主要實現(xiàn)代碼是FragmentManageImpl的saveAllState()和restoreAllState()方法。

FragmentActivity的onSaveInstance()方法中會間接調(diào)用FragmentManageImpl的saveAllState()方法,而onCreate(Bundle)中會間接調(diào)用restoreAllState()。在Fragment的onCreate(Bundle)等初始化生命方法中會傳入保存狀態(tài)的Bundle,以供開發(fā)者恢復存儲的數(shù)據(jù)。而Fragment中view的狀態(tài)的恢復則是在回調(diào)了onCreateView()后得到開發(fā)者返回的view,手動觸發(fā)view狀態(tài)的恢復:

// FragmentManageImpl.class
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
  ...
  if(f.mState < newState) { // 狀態(tài)升
     ...
    switch(f.mState) {
      ...
      case Fragment.CREATED:
        ...
        f.mView = f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
        ...
        f.performActivityCreated(f.mSavedFragmentState);
        ...
        if (f.mView != null) {
          f.restoreViewState(f.mSavedFragmentState);
        }
        f.mSavedFragmentState = null;
    }
    ...
  }
  ...
}

// Fragment.class
  final void restoreViewState(Bundle savedInstanceState) {
        if (mSavedViewState != null) {
            mInnerView.restoreHierarchyState(mSavedViewState);
            mSavedViewState = null;
        }
        mCalled = false;
        onViewStateRestored(savedInstanceState);
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onViewStateRestored()");
        }
    }

Fragment常見錯誤:

可以參考:http://www.itdecent.cn/p/d9143a92ad94

getActivity空指針錯誤

狀態(tài)沒保存、值丟失

Can not perform this action after onSaveInstanceState異常

Fragment類必須提供無參構(gòu)造方法

Fragment重疊顯示異常

其他

Fragment中還有回退棧、動畫、Android5.0中的共享元素及Activity動畫、Fragment.retainInstance屬性、LoaderManager、ChildFragmentManage等知識點這里先不討論了。

參考
從源碼角度剖析Fragment核心知識點

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容