Fragment源碼簡析(一)

先看使用Fragment的簡單例子级及,這里用Android support v4的源碼:

Fragment fragment= new Fragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.container,fragment).commit();

進入FragmentActivity的getSupportFragmentManager方法:

public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
}
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

可以看到FragmentManager是從FragmentController里取得的响迂,那么看看FragmentController里有什么:

public class FragmentController {
     public static final FragmentController 
         createController(FragmentHostCallback<?> callbacks) {
           return new FragmentController(callbacks);
     }
      public FragmentManager getSupportFragmentManager() {
           return mHost.getFragmentManagerImpl();
     }
     .........
}

又來了新類FragmentHostCallback,里面有個mFragmentManager變量:

public abstract class FragmentHostCallback<E> extends FragmentContainer {
       final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
       .........
}

看到這里奇昙,只要知道FragmentManager最后來自于FragmentManagerImpl就可以了娜饵,看名字知道是FragmentManager的實現(xiàn)類。
下面進入beginTransaction方法全封,在FragmentManagerImpl里找到:

public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
}

beginTransaction的作用就是返回一個BackStackRecord對象缺厉,可見事務辦理都是交給了BackStackRecord這個類,這個類值得注意。
繼續(xù)往下走恋拍,例子只是進行了add這個事務垛孔。查看BackStackRecord類里的add方法,有幾個重載方法施敢,最后都走到了其中一個周荐,有空可以查看所有的源碼,這里的主干代碼是:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;
        fragment.mTag = tag;
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
        addOp(new Op(opcmd, fragment));//這里的opcmd是OP_ADD
    }

只是設置了fragment的成員變量僵娃,然后調用addOp概作,注意opcmd,對fragment的操作有很多默怨,這里是OP_ADD讯榕,常用的還有OP_REPLACE,OP_REMOVE匙睹,OP_HIDE愚屁,OP_SHOW等。addOp方法干了啥:

void addOp(Op op) {
        mOps.add(op);
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
}
ArrayList<Op> mOps = new ArrayList<>();

動畫相關的不管痕檬,這步操作只是簡單的將封裝的ADD事務插入到ArrayList mOps里■保現(xiàn)在準備工作做好了,到了最后一步梦谜,提交事務丘跌,調用commit()方法:

public int commit() {
      return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
}

這里流程只關注最后一句,由FragmentManager將之前的ADD操作加入隊列唁桩。
一句話總結下上面的流程:將新建Fragment的事務封裝成ADD操作交給FragmentManager,FragmentManager將ADD操作入隊列闭树。
下面看看入隊列做了什么:

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {//PendingActions 是空,就新建一個
                mPendingActions = new ArrayList<>();
            }
            mPendingActions.add(action);//ADD操作加入ArrayList
            scheduleCommit();
        }
    }

enqueue意思是入隊荒澡,但是這里其實是個列表蔼啦,不用在意這些細節(jié)。加入隊列仰猖,后面scheduleCommit的名字看起來就是提交這個列表捏肢,執(zhí)行列表里的各個操作了,看看源碼:

private void scheduleCommit() {
        synchronized (this) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

又是通過Handler機制傳遞消息的饥侵。Handler從哪來的鸵赫?mHost是什么?最開始的FragmentActivity里有個mHandler:

public class FragmentActivity extends BaseFragmentActivityApi16 implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
         final FragmentController mFragments = 
         FragmentController.createController(new HostCallbacks());//mHost就是HostCallbacks躏升,是FragmentHostCallback子類  
         final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
             ............
            }
         }

        };
         ......
}

//activity.mHandler,Handler在這里
public abstract class FragmentHostCallback<E> extends FragmentContainer {
   FragmentHostCallback(FragmentActivity activity) {
        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);//mHandler就是FragmentActivity里的Handler
    }
    .........
}

好了辩棒,這里我們知道了mHost.getHandler().post(mExecCommit)的Handler是來自于FragmentActivity。這里有個問題scheduleCommit是在FragmentManager里調用的膨疏,F(xiàn)ragmentManager的mHost為什么就是FragmentController的mHost呢一睁?之后再分析,先看主流程,繼續(xù)上面的:

   mHost.getHandler().post(mExecCommit);//mExecCommit是個Runnable
   Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    /**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        ensureExecReady(true);

        boolean didSomething = false;
        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
            } finally {
                cleanupExec();
            }
            didSomething = true;
        }

        doPendingDeferredStart();
        burpActive();

        return didSomething;
    }

private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isPop) {
        boolean didSomething = false;
        synchronized (this) {
            if (mPendingActions == null || mPendingActions.size() == 0) {
                return false;
            }

            final int numActions = mPendingActions.size();
            for (int i = 0; i < numActions; i++) {
                didSomething |= mPendingActions.get(i).generateOps(records, isPop);//開始跑OP了
            }
            mPendingActions.clear();
            mHost.getHandler().removeCallbacks(mExecCommit);
        }
        return didSomething;
    }

這里mPendingActions.get(i).generateOps(records, isPop)的generateOps是個接口方法佃却,找到mPendingActions列表中的元素是哪個類者吁,看看generateOps具體實現(xiàn),這里的mPendingActions.get(i)就是上面的ADD操作,是個BackStackRecord對象,所以看看BackStackRecord的generateOps方法:

/**
     * Implementation of {@link FragmentManagerImpl.OpGenerator}.
     * This operation is added to the list of pending actions during {@link #commit()}, and
     * will be executed on the UI thread to run this FragmentTransaction.
     *
     * @param records Modified to add this BackStackRecord
     * @param isRecordPop Modified to add a false (this isn't a pop)
     * @return true always because the records and isRecordPop will always be changed
     */
    @Override
final class BackStackState implements Parcelable {
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        records.add(this);//ADD加入records
        isRecordPop.add(false);
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
        return true;
    }
    ...........
}

看到這里知道了饲帅,generateOpsForPendingActions只是將mPendingActions里的元素加入到了mTmpRecords复凳。那真正執(zhí)行操作的應該就是removeRedundantOperationsAndExecute方法了:

//只貼主要代碼
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop) {
       .............
      final int numRecords = records.size();
      int startIndex = 0;
      for (int recordNum = 0; recordNum < numRecords; recordNum++) {
          if (startIndex != recordNum) {
                 executeOpsTogether(records, isRecordPop, startIndex, recordNum);
           }
       }
       .............
}

這里面又有很多調用,但是最終會回到每個BackStackRecord的executeOps()方法灶泵,executeOps就是個按操作類型執(zhí)行各種具體動作的方法育八,比如OP_ADD,OP_REMOVE等等赦邻,看看OP_ADD操作干了什么:

   void executeOps() {
          final Fragment f = op.fragment;
           switch (op.cmd) {
                case OP_ADD:
                    f.setNextAnim(op.enterAnim);
                    mManager.addFragment(f, false); //第二個參數(shù)false髓棋,又回到了FragmentManager
                    break;
               .............
              }
          if (!mReorderingAllowed) {
           // Added fragments are added at the end to comply with prior behavior.
            mManager.moveToState(mManager.mCurState, true);
        }
    }     
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
    final ArrayList<Fragment> mAdded = new ArrayList<>();
     public void addFragment(Fragment fragment, boolean moveToStateNow) 
     {
        if (DEBUG) Log.v(TAG, "add: " + fragment);
        makeActive(fragment);
        if (!fragment.mDetached) {
            if (mAdded.contains(fragment)) {
                throw new IllegalStateException("Fragment already added: " + fragment);
            }
            synchronized (mAdded) {
                mAdded.add(fragment);
            }
            fragment.mAdded = true;
            fragment.mRemoving = false;
            if (fragment.mView == null) {
                fragment.mHiddenChanged = false;
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            if (moveToStateNow) {//前面?zhèn)鱽淼氖莊alse,注意
                moveToState(fragment);
            }
        }
    }
    //具體的Fragment操作都在FragmentManager里
    public void removeFragment(Fragment fragment) {
     ..........
    }
    public void hideFragment(Fragment fragment) {
       ........
    }
}

原來FragmentManager不過是將Fragment加入到自身的mAdded列表或者操作Fragment自身的屬性惶洲,比如隱藏屬性mHiddenChanged按声,具體查看其他幾個方法就知道了。

再總結下上面一堆代碼:將ADD操作加入FragmentManager的mPendingActions列表后提交湃鹊,經(jīng)過Handler機制在主線程中儒喊,執(zhí)行ADD操作的executeOps方法,將新的Fragment插入到FragmentManager的mAdded列表币呵。

走到這里怀愧,準備工作都做好了,下面該Fragment的生命周期上場了余赢。因為傳到addFragment的moveToStateNow是false,所以addFragment方法本身并沒有走生命周期流程芯义,回到executeOps方法,看到在最后調用了 mManager.moveToState。

下一篇Fragment源碼簡析(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末妻柒,一起剝皮案震驚了整個濱河市扛拨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌举塔,老刑警劉巖绑警,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件求泰,死亡現(xiàn)場離奇詭異,居然都是意外死亡计盒,警方通過查閱死者的電腦和手機渴频,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來北启,“玉大人卜朗,你說我怎么就攤上這事」敬澹” “怎么了场钉?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長懈涛。 經(jīng)常有香客問我逛万,道長,這世上最難降的妖魔是什么肩钠? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任泣港,我火速辦了婚禮,結果婚禮上价匠,老公的妹妹穿的比我還像新娘当纱。我一直安慰自己,他們只是感情好踩窖,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布坡氯。 她就那樣靜靜地躺著,像睡著了一般洋腮。 火紅的嫁衣襯著肌膚如雪箫柳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天啥供,我揣著相機與錄音悯恍,去河邊找鬼。 笑死伙狐,一個胖子當著我的面吹牛涮毫,可吹牛的內容都是我干的。 我是一名探鬼主播贷屎,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼罢防,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唉侄?” 一聲冷哼從身側響起咒吐,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后恬叹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體候生,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年妄呕,在試婚紗的時候發(fā)現(xiàn)自己被綠了陶舞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡绪励,死狀恐怖,靈堂內的尸體忽然破棺而出唠粥,到底是詐尸還是另有隱情疏魏,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布晤愧,位于F島的核電站大莫,受9級特大地震影響,放射性物質發(fā)生泄漏官份。R本人自食惡果不足惜只厘,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舅巷。 院中可真熱鬧羔味,春花似錦、人聲如沸钠右。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽飒房。三九已至搁凸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狠毯,已是汗流浹背护糖。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嚼松,地道東北人嫡良。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像惜颇,于是被迫代替她去往敵國和親皆刺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350