Android-Fragment簡(jiǎn)要分析

注意:文章對(duì)Fragment源碼的分析基于support v4的Fragment包扫外,版本號(hào)為25.3.1

Fragment相關(guān)類(lèi)UML圖

1008428-b84d45b1ba19d73d.jpg

support包中對(duì)負(fù)責(zé)管理Fragment生命是FragmentActivity债沮,v7包的AppCompatActivity也是繼承于它终议。
FragmentActivity管理Fragment是通過(guò)它內(nèi)部mFragments的變量,mFragments類(lèi)型為FragmentController.

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

FragmentActivity傳遞自身生命狀態(tài)难衰、狀態(tài)保存、恢復(fù)都是通過(guò)調(diào)用mFragments相應(yīng)的方法進(jìn)行處理;

@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本身只是簡(jiǎn)單的將相應(yīng)請(qǐng)求轉(zhuǎn)發(fā)給FragmentManagerImpl岂津,只是起到了橋梁溝通作用。

FragmentActivity創(chuàng)建mFragments時(shí)傳遞了一個(gè)HostCallbacks內(nèi)部類(lèi)實(shí)例悦即,這個(gè)callback對(duì)象給Fragment提供了獲取Context吮成,啟動(dòng)Activity的能力。

Fragment

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

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

View:Fragment管理View所依賴(lài)的相關(guān)成員變量粉洼。

// 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)變化中進(jìn)行回調(diào)的。

Fragment的事務(wù):FragmentTransaction宵喂、BackStackRecord

添加一個(gè)Fragment糠赦,我們一般使用如下代碼:

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

FragmentTransaction表示一個(gè)事務(wù),提供了如add锅棕、remove拙泽、attach、detach裸燎、show顾瞻、hide、replace等接口操控Fragment德绿。它的具體實(shí)現(xiàn)類(lèi)是BackStackRecord荷荤,它的接口實(shí)現(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等方法最后都是生成一個(gè)Op對(duì)象保存在mOps列表中移稳,Op是Operator簡(jiǎn)寫(xiě)表示操作蕴纳,Op的cmd屬性則區(qū)分操作的類(lèi)型,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事務(wù)了

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

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

事務(wù)執(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í)行相應(yīng)的FragmentManage方法,最后將Fragment的狀態(tài)移動(dòng)的相應(yīng)的狀態(tài)忱屑。

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

Fragment添加到FragmentMange中后蹬敲,要將它移動(dòng)到相應(yīng)的狀態(tài),使Fragment正確的顯示莺戒、交互伴嗡。

FragmentManage的成員變量mCurState存儲(chǔ)著當(dāng)前Activity狀態(tài)對(duì)應(yīng)的Fragment狀態(tài)值,它的取值范圍如同F(xiàn)ragment从铲。Activity的狀態(tài)發(fā)生變化時(shí)都會(huì)觸發(fā)相應(yīng)方法修改mCurState瘪校,并將所有的active 的Fragment移動(dòng)到相應(yīng)的狀態(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方法就負(fù)責(zé)了將自己管理的所有Fragment的狀態(tài)移動(dòng)到對(duì)應(yīng)的狀態(tài)上名段,當(dāng)mCurState變化阱扬,或者Fragment操作觸發(fā)都會(huì)調(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處于前臺(tái)静稻。 所以moveToState內(nèi)部分兩條線(xiàn)警没,狀態(tài)躍升,和狀態(tài)降低姊扔,里面各有一個(gè)switch判斷惠奸,注意到switch里每個(gè)case都沒(méi)有break梅誓,這意味著恰梢,狀態(tài)可以持續(xù)變遷,比如從INITIALIZING梗掰,一直躍升到RESUMED嵌言,將每個(gè)case都走一遍,每次case語(yǔ)句內(nèi)及穗,都會(huì)改變state的值摧茴。

Untitled.png

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

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

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

// 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常見(jiàn)錯(cuò)誤:

可以參考:http://www.reibang.com/p/d9143a92ad94

getActivity空指針錯(cuò)誤

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

Can not perform this action after onSaveInstanceState異常

Fragment類(lèi)必須提供無(wú)參構(gòu)造方法

Fragment重疊顯示異常

其他

Fragment中還有回退棧躏率、動(dòng)畫(huà)躯畴、Android5.0中的共享元素及Activity動(dòng)畫(huà)、Fragment.retainInstance屬性薇芝、LoaderManager蓬抄、ChildFragmentManage等知識(shí)點(diǎn)這里先不討論了。

參考
從源碼角度剖析Fragment核心知識(shí)點(diǎn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末夯到,一起剝皮案震驚了整個(gè)濱河市嚷缭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耍贾,老刑警劉巖峭状,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逼争,居然都是意外死亡优床,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)誓焦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)胆敞,“玉大人,你說(shuō)我怎么就攤上這事杂伟∫撇悖” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵赫粥,是天一觀(guān)的道長(zhǎng)观话。 經(jīng)常有香客問(wèn)我,道長(zhǎng)越平,這世上最難降的妖魔是什么频蛔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮秦叛,結(jié)果婚禮上晦溪,老公的妹妹穿的比我還像新娘。我一直安慰自己挣跋,他們只是感情好三圆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般舟肉。 火紅的嫁衣襯著肌膚如雪修噪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天路媚,我揣著相機(jī)與錄音割按,去河邊找鬼。 笑死磷籍,一個(gè)胖子當(dāng)著我的面吹牛适荣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播院领,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼弛矛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了比然?” 一聲冷哼從身側(cè)響起丈氓,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎强法,沒(méi)想到半個(gè)月后万俗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饮怯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年闰歪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓖墅。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡库倘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出论矾,到底是詐尸還是另有隱情教翩,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布贪壳,位于F島的核電站饱亿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏闰靴。R本人自食惡果不足惜彪笼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望传黄。 院中可真熱鬧杰扫,春花似錦队寇、人聲如沸膘掰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)识埋。三九已至凡伊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窒舟,已是汗流浹背系忙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惠豺,地道東北人银还。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像洁墙,于是被迫代替她去往敵國(guó)和親蛹疯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容