Fragment事務管理源碼分析

概述

在Fragment使用中,有時候須要對Fragment進行addremoveshowhidereplace等操做來進行Fragment的顯示隱藏等管理,這些管理是經過FragmentTransaction進行事務管理的。事務管理是對於一系列操做進行管理,一個事務包含一個或多個操做命令,是邏輯管理的工做單元。一個事務開始於第一次執行操做語句,結束於Commit。通俗地將,就是把多個操做緩存起來,等調用commit的時候,統一批處理。下面會對Fragmeng的事務管理作一個代碼分析java

分析入口

/** * 顯示Fragment,若是Fragment已添加過,則直接show,不然構造一個Fragment * * @param containerViewId 容器控件id * @param clz Fragment類 */
    protected void showFragment(@IdRes int containerViewId, Class<? extends Fragment> clz) {
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();//開始事務管理
        Fragment f;
        if ((f = fm.findFragmentByTag(clz.getName())) == null) {
            try {
                f = clz.newInstance();
                ft.add(containerViewId, f, clz.getName());//添加操做
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            ft.show(f);//添加操做
        }
        ft.commit();//提交事務
    }
複製代碼

上面是一個簡單的顯示Fragment的栗子,簡單判斷一下Fragment是否已添加過,添加過就直接show,不然構造一個Fragment,最後提交事務。緩存

代碼分析

FragmentManager

2019-9-2-11-19-20.png
上圖是獲取FragmentManager的大致過程

要管理Fragment事務,首先是須要拿到FragmentManager,在Activity中能夠經過getFragmentManager()方法獲取(使用兼容包的話,經過FragmentActivity#getSupportFragmentManager()),在這裏咱們就不對兼容包進行分析了ide

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    
    /** * Return the FragmentManager for interacting with fragments associated * with this activity. */
    public FragmentManager getFragmentManager() {
        return mFragments.getFragmentManager();
    }
複製代碼

FragmentManager是一個抽象類,它是經過mFragments.getFragmentManager()來獲取的,mFragments是FragmentController對象,它經過FragmentController.createController(new HostCallbacks())生成,這是一個靜態工廠方法:oop

public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
        return new FragmentController(callbacks);
    }
複製代碼

在這裏面直接new了一個FragmentController對象,注意FragmentController的構造方法須要傳入一個FragmentHostCallback源碼分析

FragmentController構造方法

private final FragmentHostCallback<?> mHost;
    private FragmentController(FragmentHostCallback<?> callbacks) {
        mHost = callbacks;
    }
複製代碼

構造方法很簡單,傳入了一個FragmentHostCallback實例post

FragmentController#getFragmentManager

public FragmentManager getFragmentManager() {
        return mHost.getFragmentManagerImpl();
    }
複製代碼

這裏又調用了mHost的getFragmentManagerImpl方法,但願童鞋們沒有被繞暈,mHost是一個FragmentHostCallback實例,那咱們回過頭來看看它傳進來的地方動畫

FragmentHostCallback

這個FragmentHostCallback是一個抽象類,咱們能夠看到,在Activity中是傳入了 Activity#HostCallbacks內部類,這個就是FragmentHostCallback的實現類this

FragmentHostCallback#getFragmentManagerImpl

final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    FragmentManagerImpl getFragmentManagerImpl() {
        return mFragmentManager;
    }
複製代碼

終於找到FragmentManager的真身FragmentManagerImplspa

FragmentManagerImpl#beginTransaction

@Override
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }
複製代碼

能夠看到,所謂的FragmentTransaction其實就是一個BackStackRecord。到如今,FragmentManager和FragmentTransaction咱們都找到了。下圖就是各個類之間的關係: .net

2019-9-2-11-20-42.png

下面開始真正的事務管理分析,咱們先選擇一個事務add來進行分析

FragmentTransaction#add

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);
        return this;
    }
    
    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    
        //設置fragment的FragmentManagerImpl,mManager其實就是Activity#HostCallbacks中的成員變量
        fragment.mFragmentManager = mManager;

        //設置fragment的tag
        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("...");
            }
            fragment.mTag = tag;
        }

        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("...");
            }
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("");
            }
            //設置fragment的mContainerId以及mFragmentId
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }
        
        //新增一個操做
        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        //添加操做
        addOp(op);
    }
    
    //插入到鏈表的最後
    void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }
複製代碼

add的操做步驟爲:

  1. 設置fragment的FragmentManagerImpl
  2. 設置fragment的tag
  3. 設置fragment的mContainerId以及mFragmentId
  4. 插入一個類型爲OP_ADD的操做到鏈表最後

這裏用到了一個類:

static final class Op {
        Op next;//下一操做節點
        Op prev;//上一操做節點
        int cmd;//操做類型,可選有:OP_NULL|OP_ADD|OP_REPLACE|OP_REMOVE|OP_HIDE|OP_SHOW|OP_DETACH|OP_ATTACH
        Fragment fragment;//操做的Fragment對象
        int enterAnim;//入場動畫
        int exitAnim;//出場動畫
        int popEnterAnim;//彈入動畫
        int popExitAnim;//彈出動畫
        ArrayList<Fragment> removed;
    }
複製代碼

這是一個操做鏈表節點。全部add、remove、hide等事物最終會造成一個操做鏈

FragmentTransaction#commit

等全部操做都插入後,最後咱們須要調用FragmentTransaction的commit方法,操做纔會真正地執行。

public int commit() {
        return commitInternal(false);
    }
    
    int commitInternal(boolean allowStateLoss) {
        //防止重複commit
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        
        //DEBUG代碼通通無論
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
            dump(" ", null, pw, null);
            pw.flush();
        }
        
        mCommitted = true;
        
        //只有調用了addToBackStack方法以後,這個標記纔會爲true
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        //插入事物隊列
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
複製代碼

FragmentManagerImpl#enqueueAction

/** * Adds an action to the queue of pending actions. * * @param action the action to add * @param allowStateLoss whether to allow loss of state information * @throws IllegalStateException if the activity has been destroyed */
    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }
複製代碼

這裏把操做添加到mPendingActions列表裏去。並經過mHost.getHandler()獲取Handler發送執行請求。從上面的分析知道,mHost就是Activity的HostCallbacks,構造方法中把Activity的mHandler傳進去了,這裏執行的mHost.getHandler()獲取到的也就是Activity中的mHandler,這樣作是由於須要在主線程中執行

final Handler mHandler = new Handler();
複製代碼

再看看mExecCommit中作了什麼操做:

Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    
    /** * Only call from main thread! */
    public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }
        
        //再次檢測是否主線程
        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
            throw new IllegalStateException("Must be called from main thread of process");
        }

        boolean didSomething = false;

        while (true) {
            int numActions;
            
            synchronized (this) {
            
                //參數檢測
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }
                
                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            
            mExecutingActions = true;
            //遍歷執行待處理的事務操做
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }

        doPendingDeferredStart();

        return didSomething;
    }
複製代碼

插入了事物以後,就是在主線程中把須要處理的事務統一處理,處理事務是經過執行mTmpActions[i].run()進行的,這個mTmpActions[i]就是前面咱們經過enqueueAction方法插入的BackStackRecord,童鞋們可能沒注意到,它但是一個Runnable,咱們來看看它的定義

final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable {
    static final String TAG = FragmentManagerImpl.TAG;
    
    ... ...
}
複製代碼

兜兜轉轉,咱們又回到了BackStackRecord

BackStackRecord#run

public void run() {
    
        ......
        
        if (mManager.mCurState >= Fragment.CREATED) {
            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
            calculateFragments(firstOutFragments, lastInFragments);
            beginTransition(firstOutFragments, lastInFragments, false);
        }
        //遍歷鏈表,根據cmd事務類型依次處理事務
        Op op = mHead;
        while (op != null) {
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.addFragment(f, false);
                }
                break;
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
                            Fragment old = mManager.mAdded.get(i);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = op.exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                    }
                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
                                }
                            }
                        }
                    }
                    if (f != null) {
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    }
                }
                break;
                case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.removeFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_HIDE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.hideFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_SHOW: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.showFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_DETACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.detachFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_ATTACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.attachFragment(f, mTransition, mTransitionStyle);
                }
                break;
                default: {
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
            }

            op = op.next;
        }

        mManager.moveToState(mManager.mCurState, mTransition,
                mTransitionStyle, true);

        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }
複製代碼

到這一步,提交的事務就被真正執行了,咱們知道,即便commit了事務以後,也不是同步執行的,是經過Handler發送到主線程執行的。

全部事務的處理都是在run方法裏面執行,可是咱們留意到,想要搞清楚add、remove等事務背後真正作了什麼,還須要深刻了解FragmentManagerImpl。

本文主要講解Fragment事務的流程,FragmentManagerImpl的分析準備放到下一篇分析文章Fragment源碼分析中,相信經過分析以後,就能夠對Fragment的生命週期也有一個很好的認識了

相關文章
相關標籤/搜索