Fragment流程淺析

順其自然55發表於2018-02-12
先從fragment基本用法出發:

getSupportFragmentManager().beginTransaction()
           .add(R.id.basic, new PageFragment()).commit();複製程式碼
其中點進去getSupportFragmentManager()可以看到跳轉到FragmentActivity:

public FragmentManager getSupportFragmentManager() {     
    return mFragments.getSupportFragmentManager();  
}複製程式碼
先看下mFragments是啥:

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {    
      return new FragmentController(callbacks);
}複製程式碼
往下看.

private FragmentController(FragmentHostCallback<?> callbacks) {  
  mHost = callbacks;
}複製程式碼
public FragmentManager getSupportFragmentManager() {
    return mHost.getFragmentManagerImpl();
}複製程式碼
看樣子是呼叫了FragmentHostCallback的getFragmentManagerImpl(),往下看HostCallbaks的程式碼:
class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
    public HostCallbacks() {
        super(FragmentActivity.this /*fragmentActivity*/);
    }

    @Override
    public LayoutInflater onGetLayoutInflater() {
        return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
    }

    //程式碼省略...
    @Override
    public FragmentActivity onGetHost() {
        return FragmentActivity.this;
    }

    @Override
    public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
    }

     //程式碼省略...
   
    @Override
    public void onStartActivityFromFragment(
            Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
    }

    @Override
    public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
            @NonNull String[] permissions, int requestCode) {
        FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
                requestCode);
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        FragmentActivity.this.onAttachFragment(fragment);
    }

    //程式碼省略...
    @Nullable
    @Override
    public View onFindViewById(int id) {
        return FragmentActivity.this.findViewById(id);
    }
    //程式碼省略...
}複製程式碼
先有個印象,這裡還有startActivity這樣的方法,其實Fragment中啟動的方法都是呼叫這裡的,而且HostCallbacks是FragmentActivity的內部類,所以構造方法直接持有了Activity引用.
這裡沒找到getSupportFragmentManager()方法,再看看它的父類:
public abstract class FragmentHostCallback<E> extends FragmentContainer {
    private final Activity mActivity;
    final Context mContext;
    private final Handler mHandler;
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    
     //省略大部分程式碼
    public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
        this(context instanceof Activity ? (Activity) context : null, context, handler,
                windowAnimations);
    }

    FragmentHostCallback(FragmentActivity activity) {
        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
    }

    FragmentHostCallback(Activity activity, Context context, Handler handler,
            int windowAnimations) {
        mActivity = activity;
        mContext = context;
        mHandler = handler;
        mWindowAnimations = windowAnimations;
    }

    /**
     * Return a {@link LayoutInflater}.
     * See {@link Activity#getLayoutInflater()}.
     */
    public LayoutInflater onGetLayoutInflater() {
        return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    Activity getActivity() {
        return mActivity;
    }

    Context getContext() {
        return mContext;
    }

    Handler getHandler() {
        return mHandler;
    }

    FragmentManagerImpl getFragmentManagerImpl() {
        return mFragmentManager;
    }
}複製程式碼
可以看到子類沒有重寫getSupportFragmentManager()方法,所以獲得的物件就是FragmentMangerImpl了.再繼續往下看beginTransaction()方法:
@Override
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}複製程式碼
又可以看到beginTransaction()返回的物件是BacStateRecord,繼續往下看
@Override
public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}

@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

@Override
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) {
    final Class fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    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.");
    }

    fragment.mFragmentManager = mManager;

    if (tag != null) {
        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) {
        if (containerViewId == View.NO_ID) {
            throw new IllegalArgumentException("Can't add fragment "
                    + fragment + " with tag " + tag + " to container view with no id");
        }
        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;
    }

    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}

@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
    return replace(containerViewId, fragment, null);
}

@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
    if (containerViewId == 0) {
        throw new IllegalArgumentException("Must use non-zero containerViewId");
    }

    doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    return this;
}

@Override
public FragmentTransaction remove(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_REMOVE;
    op.fragment = fragment;
    addOp(op);

    return this;
}

@Override
public FragmentTransaction hide(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_HIDE;
    op.fragment = fragment;
    addOp(op);

    return this;
}

@Override
public FragmentTransaction show(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_SHOW;
    op.fragment = fragment;
    addOp(op);

    return this;
}
複製程式碼
void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}複製程式碼
所有add方法和replace方法都呼叫到doAppOp方法,然後最終都回撥到addOp方法。
先看下doAppOp方法,首先先檢測fragment是不是匿名類或者不是公共類或者是成員變數且是靜態的,就丟擲異常.
接下來給fragment賦值,首先就是賦值mFragmentManager為FragmentMangerImpl,如果使用者設定tag,則再設定fragment的tag,如果之前設定過就報錯,mContainerId同理,接下來看Op這個類:
static final class Op {
    int cmd;
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;
}複製程式碼

就幾個成員變數,cmd都是設為1,然後放入fragment,然後放入設定的動畫.show和hide也呼叫該方法,然後看最後一步commit:

@Override
public int commit() {
    return commitInternal(false);
}

@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}

@Override
public void commitNow() {
    disallowAddToBackStack();
    mManager.execSingleAction(this, false);
}

@Override
public void commitNowAllowingStateLoss() {
    disallowAddToBackStack();
    mManager.execSingleAction(this, true);
}

@Override
public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
    mAllowOptimization = allowOptimization;
    return this;
}

int commitInternal(boolean allowStateLoss) {
    if (mCommitted) throw new IllegalStateException("commit already called");
    if (FragmentManagerImpl.DEBUG) {
        Log.v(TAG, "Commit: " + this);
        LogWriter logw = new LogWriter(TAG);
        PrintWriter pw = new PrintWriter(logw);
        dump("  ", null, pw, null);
        pw.close();
    }
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex; 複製程式碼
commit操作都呼叫了commitInternal方法,這裡有個mCommit控制,如果同一個BackStackRecord物件第二次再commit,是會報錯的,DEBUG模式下列印log,
mAddToBackStack賦值在以下程式碼:
@Override
public FragmentTransaction disallowAddToBackStack() {
    if (mAddToBackStack) {
        throw new IllegalStateException(
                "This transaction is already being added to the back stack");
    }
    mAllowAddToBackStack = false;
    return this;
}複製程式碼
設定過回退棧名稱的 mAddToBackStack = true;
如果mAddToBackStack為true,則加入到FragmentManger的一個集合中,並返回在當前集合中的位置,程式碼如下:
public int allocBackStackIndex(BackStackRecord bse) {
    synchronized (this) {
        if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
            if (mBackStackIndices == null) {
                mBackStackIndices = new ArrayList<BackStackRecord>();
            }
            int index = mBackStackIndices.size();
            if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
            mBackStackIndices.add(bse);
            return index;

        } else {
            int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
            if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
            mBackStackIndices.set(index, bse);
            return index;
        }
    }
}複製程式碼
接下來執行的是mManager.enqueueAction(this, allowStateLoss),
public void enqueueAction(OpGenerator 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<>();
        }
        mPendingActions.add(action);
        scheduleCommit();
    }
}複製程式碼
這裡我們可以分析commit()和commitAllowingStateLoss()啥區別了,commit傳入的allowStateLoss的是false,而commitAllowingStateLoss()傳入的是true,兩個唯一的 不同點就是commit執行了allowStateLoss方法:
private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}複製程式碼
判斷mStateSaved是否儲存,如果儲存了則不允許再次
提交,然後報錯,也就是說commit()必須在onSaveInstanceState()方法之前呼叫,不然就是拋異常了.
繼續往下看,這裡用synchronized鎖,如果被銷燬了或者mHost為空,則報異常說activity已經銷燬了,這個mHost是持有activity的引用的.佇列為空則建立佇列,然後加入當前的action,當前action實現的方法如下:
public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
    if (FragmentManagerImpl.DEBUG) {
        Log.v(TAG, "Run: " + this);
    }

    records.add(this);
    isRecordPop.add(false);
    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    }
    return true;
}複製程式碼
其實就是加入FragmentManager的佇列而已.繼續看:
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);
        }
    }
}複製程式碼
mHost.getHandler()其實就是FragmentActivity中的mHandler了,先移除,確保沒有該Callback,然後加入Callback.看下具體實現:
Runnable mExecCommit = new Runnable() {
    @Override
    public void run() {
        execPendingActions();
    }
};複製程式碼
public boolean execPendingActions() {
    ensureExecReady(true);

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

    doPendingDeferredStart();

    return didSomething;
}複製程式碼
再回頭看下commitNow()和commitNowAllowingStateLoss()方法,就是設定禁止加入返回棧,然後execSingleAction方法和execPendingActions()幾乎一模一樣,從這裡看出commit和commitNow()的區別就是一個需要handler佇列來執行,一個直接執行,allowStateLoss還是代表是否檢測.
@Override
public FragmentTransaction disallowAddToBackStack() {
    if (mAddToBackStack) {
        throw new IllegalStateException(
                "This transaction is already being added to the back stack");
    }
    mAllowAddToBackStack = false;
    return this;
}
複製程式碼
public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
    ensureExecReady(allowStateLoss);
    if (action.generateOps(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
        } finally {
            cleanupExec();
        }
    }

    doPendingDeferredStart();
}複製程式碼
看下generateOpsForPendingActions(mTmpRecords, mTmpIsPop):
private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isPop) {
    int numActions;
    synchronized (this) {
        if (mPendingActions == null || mPendingActions.size() == 0) {
            return false;
        }

        numActions = mPendingActions.size();
        for (int i = 0; i < numActions; i++) {
            mPendingActions.get(i).generateOps(records, isPop);
        }
        mPendingActions.clear();
        mHost.getHandler().removeCallbacks(mExecCommit);
    }
    return numActions > 0;
}複製程式碼
​其實就是執行了上面的generateOps方法,而且mPendingActions執行結束被清空了。
其他程式碼有點繁多,自己有情趣可以去看看,現在重點來看 doPendingDeferredStart():
void doPendingDeferredStart() {
    if (mHavePendingDeferredStart) {
        boolean loadersRunning = false;
        for (int i = 0; i < mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null && f.mLoaderManager != null) {
                loadersRunning |= f.mLoaderManager.hasRunningLoaders();
            }
        }
        if (!loadersRunning) {
            mHavePendingDeferredStart = false;
            startPendingDeferredFragments();
        }
    }
}複製程式碼
void startPendingDeferredFragments() {
    if (mActive == null) return;

    for (int i=0; i<mActive.size(); i++) {
        Fragment f = mActive.get(i);
        if (f != null) {
            performPendingDeferredStart(f);
        }
    }
}複製程式碼
public void performPendingDeferredStart(Fragment f) {
    if (f.mDeferStart) {
        if (mExecutingActions) {
            // Wait until we're done executing our pending transactions
            mHavePendingDeferredStart = true;
            return;
        }
        f.mDeferStart = false;
        moveToState(f, mCurState, 0, 0, false);
    }
}複製程式碼
由於能力有限,就直接講最重要的 moveToState(f, mCurState, 0, 0, false)方法了.
講這個之前,首先得先講Fragment如何和Activity生命週期聯絡起來.
看下FragmentActivity的onCreate方法:
protected void onCreate(@Nullable Bundle savedInstanceState) {
      //程式碼省略..
      mFragments.dispatchCreate();
}

public void dispatchCreate() {
      mHost.mFragmentManager.dispatchCreate();
}

public void dispatchCreate() {
      mStateSaved = false;
      mExecutingActions = true;
      moveToState(Fragment.CREATED, false);
      mExecutingActions = false;
}

void moveToState(int newState, boolean always) {
      //程式碼省略..
      mCurState = newState;
      //程式碼省略..
      if (!loadersRunning) {
         startPendingDeferredFragments();
      }
     //程式碼省略..
}

mCurState就是activity當前的生命週期了,然後又呼叫startPendingDeferredFragments()最終還是到moveToState(f, mCurState, 0, 0, false)方法了,其他生命週期同理.
moveToState程式碼量有點多,我就省略大部分程式碼了,所以只講下create流程了.
case Fragment.CREATED:
    if (newState > Fragment.CREATED) {
        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
        if (!f.mFromLayout) {
            ViewGroup container = null;

             //mContainerId就是add(int containerId,Fragment fragment)傳進來的佈局id,
            if (f.mContainerId != 0) {
                if (f.mContainerId == View.NO_ID) {
                    throwException(new IllegalArgumentException(
                            "Cannot create fragment "
                                    + f
                                    + " for a container view with no id"));
                }

//mContainer.onFindViewById等同HostCallbacks.onFindViewById(int id),
//也就是FragmentActivity.this.findViewById(id)                       container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                if (container == null && !f.mRestored) {
                    String resName;
                    try {
                        resName = f.getResources().getResourceName(f.mContainerId);
                    } catch (NotFoundException e) {
                        resName = "unknown";
                    }
                    throwException(new IllegalArgumentException(
                            "No view found for id 0x"
                            + Integer.toHexString(f.mContainerId) + " ("
                            + resName
                            + ") for fragment " + f));
                }
            }
            f.mContainer = container;

            //執行onCreateView方法,
            f.mView = f.performCreateView(f.getLayoutInflater(
                    f.mSavedFragmentState), container, f.mSavedFragmentState);
            if (f.mView != null) {
                f.mInnerView = f.mView;
                if (Build.VERSION.SDK_INT >= 11) {
                    ViewCompat.setSaveFromParentEnabled(f.mView, false);
                } else {
                    f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                }

//這裡把返回的view新增進activity的container中,這裡引出一個問題,以前碰到過在
// onCreateView中設定View view = inflater.inflate(R.layout.fragment_page, container, //true);
//會直接蹦,原因就在這裡了,因為設定為true就將子類新增進父類了,然後這裡又進行一次新增操作,就丟擲異常了                if (container != null) {
                    container.addView(f.mView);
                }
                if (f.mHidden) {
                    f.mView.setVisibility(View.GONE);
                }
                
                //執行onViewCreated方法
                f.onViewCreated(f.mView, f.mSavedFragmentState);

                //這裡如果使用者設定過registerFragmentLifecycleCallbacks,就回撥對應的方法.
                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                        false);
                // Only animate the view if it is visible. This is done after
                // dispatchOnFragmentViewCreated in case visibility is changed
                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                        && f.mContainer != null;
            } else {
                f.mInnerView = null;
            }
        }

        //執行onActivityCreated方法,然後同理回撥
        f.performActivityCreated(f.mSavedFragmentState);
        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
        if (f.mView != null) {
            f.restoreViewState(f.mSavedFragmentState);
        }
        f.mSavedFragmentState = null;
    }複製程式碼
我覺得以後多Fragment開發可能會越來越火,瞭解一下Fragment的基本流程,還是挺重要的吧。由於能力有限,我只是講下Fragment的流程的一點毛皮,如有錯誤還望指出。
順便推薦下一個大神寫的Fragment框架:github.com/YoKeyword/F…