Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Fragment 和 FragmentManager 的代碼分析

Android Fragment 和 FragmentManager 的代碼分析

編輯:關於Android編程

這兩天在研究插件化編程,在使用 Fragment 碰到了一些問題,於是查看源碼,順便分析了一下 Fragment 和 FragmentManager 以及其他幾個 API 的原代碼,看看他們是怎麼工作的。

我們知道 Fragment 有個 onCreateView() 方法,這個方法在 Fragment 創建 View 的時候被調用,並且返回一個 View 對象。那麼 onCreateView 在什麼時候被調用呢,咱們在 Fragment 這個類裡找到了一個方法,performCreateView() 方法。

Fragment.java
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
  @Nullable Bundle savedInstanceState) {
 return null;
}

performCreateView 這個方法在什麼時候會被調用呢,在 Fragment 裡找不到調用它的代碼。咱們可以猜測一下,大概會在 FragmentManager 裡。

View performCreateView(LayoutInflater inflater, ViewGroup container,
  Bundle savedInstanceState) {
 if (mChildFragmentManager != null) {
  mChildFragmentManager.noteStateNotSaved();
 }
 return onCreateView(inflater, container, savedInstanceState);
}

在 FragmentManager 裡,咱們找到了調用 Fragment.performCreateView 的代碼,在 moveToState() 方法裡,這個方法有點大,我只粘貼了部分代碼。可以看到,它會在 Fragment 初始化或者創建的時候被調用。並且我們知道,創建的View 被賦值給 Fragment 的 mView 成員變量了。

FragmentManager.java
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
 switch (f.mState) {
 case Fragment.INITIALIZING:
  if (f.mFromLayout) {
   f.mView = f.performCreateView(f.getLayoutInflater(
   f.mSavedFragmentState), null, f.mSavedFragmentState);
 }
 break;
case Fragment.CREATED:
 if (!f.mFromLayout) {
  f.mView = f.performCreateView(f.getLayoutInflater(
   f.mSavedFragmentState), container, f.mSavedFragmentState);
 }
break;
} 
}

接下來,咱們要看什麼時候會調用 moveToState() 這個方法。找了一下,發現很 N 多的地方調用了這個方法。這樣給咱們逆推找代碼造成了一定的難度。於是咱們換個思路,正推來分析。怎麼正推了,看咱們怎麼使用 Fragment 和 FragmentManager 來分析。

一般咱們都是 getFragmentManager() 或者 getSupportFragmentManager() 的方法來獲取 FragmentManager.以 FragmentActivity 為例,一般情況下,咱們在這個類的子類裡調用這兩個方法之一。

咱們在 FragmentActivity 裡找到了相應的代碼。FragmentManager 是一個抽象類,FragmentManagerImpl 是 FragmentManager 的子類,在 FragmentManager 同一個 java 文件內,是一個內部類。它是 FragmentManager 的實現。

FragmentActivity.java
//FragmentManagerImpl is subclass of FragmentManager
final FragmentManagerImpl mFragments = new FragmentManagerImpl();
public FragmentManager getSupportFragmentManager() {
 return mFragments;
}

獲取到 FragmentManager 後,咱們一般就會調用 beginTransaction() 方法,返回一個 FragmentTransaction 。咱們看代碼去。

FragmentManager.java
public abstract FragmentTransaction beginTransaction();
FragmentManagerImpl extends FragmentManager
@Override
public FragmentTransaction beginTransaction() {
 return new BackStackRecord(this);
}
/**
* Static library support version of the framework's {@link android.app.FragmentTransaction}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework SDK
* documentation for a class overview.
*/
public abstract class FragmentTransaction

我們發現 FragmentManager 是一個抽象方法,實現在 FragmentManagerImpl。FragmentManagerImpl.beginTransaction() 返回的是一個BackStackRecord,而 FragmentTransaction 是一個抽象類。那麼 BackStackRecord 是個什麼鬼。

我們找到了 BackStackRecord 這個類。我們注意到,它繼承於 FragmentTransaction,並且實現了 Runable 接口。它的方法有很多,咱們就分析一個咱們比較常用的,比如 add() 方法。

BackStackRecord.java
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable 
final FragmentManagerImpl mManager;
public BackStackRecord(FragmentManagerImpl manager) {
 mManager = manager;
}

add() 方法其實沒干啥,咱們一路追下去看。

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) {
 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 (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);
}
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++;
}

一直追到 addOp() 就斷了,好像啥事也沒干。不過它大概是在一個 add 操作添加到一個鏈表上了。那咱們怎麼辦呢?一般咱們add 完後會 commit 一下,咱們看看 commit 都干了啥。

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

commit 好像也沒干啥特殊的事情,不過可以看到這麼一行代碼 mManager.enqueueAction(this, allowStateLoss); 看 enqueueAction 這個方法名,應該會做點事情的。

同樣,咱們在 FragmentManagerImpl 裡找到了這個方法。

public void enqueueAction(Runnable action, boolean allowStateLoss) {
  if (!allowStateLoss) {
    checkStateLoss();
  }
  synchronized (this) {
    if (mDestroyed || mActivity == null) {
      throw new IllegalStateException("Activity has been destroyed");
    }
    if (mPendingActions == null) {
      mPendingActions = new ArrayList<Runnable>();
    }
    mPendingActions.add(action);
    if (mPendingActions.size() == 1) {
      mActivity.mHandler.removeCallbacks(mExecCommit);
      mActivity.mHandler.post(mExecCommit);
    }
  }
}

這個方法把咱們的 BackStackRecord -- 其實是 FragmentTransaction,也是 Runnable -- 添加到一個 mPendingActions 的 ArrayList 裡了。然後調用 mActivity.mHandler.post(mExecCommit); mExecCommit 又是什麼鬼?

Runnable mExecCommit = new Runnable() {
  @Override
  public void run() {
    execPendingActions();
  }
};
mActivity.mHandler.post(mExecCommit); 說明它在主線程裡執行了 mExecCommit 的 run 方法。別問我咋知道的。
execPendingActions() 方法稍微比較大,我把注釋寫在代碼裡。
public boolean execPendingActions() {
  if (mExecutingActions) {
    throw new IllegalStateException("Recursive entry to executePendingTransactions");
  }
  //如果不是在主線程,拋出一個異常。
  if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
    throw new IllegalStateException("Must be called from main thread of process");
  }
  boolean didSomething = false;
  // 這裡有一個 while true 循環。
  while (true) {
    int numActions;
    // 這裡在一個同步語句塊裡,把上次 mPendingActions 裡的元素轉移到 mTmpActions 數組裡。並且執行 run方法。執行誰的 run 方法呢?!就是 BackStackRecord , 也就是 FragmentTransaction 。我在最後面貼了 BackStackRecord 的 run 方法。
    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();
      mActivity.mHandler.removeCallbacks(mExecCommit);
    }
    mExecutingActions = true;
    for (int i=0; i<numActions; i++) {
      mTmpActions[i].run();
      mTmpActions[i] = null;
    }
    mExecutingActions = false;
    didSomething = true;
  }
  // 這裡有好幾行代碼,不知道干啥的,反正就是做了一些判斷,最後可能會調用 startPendingDeferredFragments() 方法。
  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();
    }
  }
  return didSomething;
}

startPendingDeferredFragments 方法又是一坨不知道啥意思的代碼。最後可能調用了 performPendingDeferredStart()

void startPendingDeferredFragments() {
  if (mActive == null) return;
  for (int i=0; i<mActive.size(); i++) {
    Fragment f = mActive.get(i);
    if (f != null) {
      performPendingDeferredStart(f);
    }
  }
}

在 這個方法裡,咱們看到了很熟悉的 moveToState() 方法。接著就是上面的分析,Fragment 的 onCreateView 會被調用。

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);
  }
}

咱們在回來看 BackStackRecord 的 run 方法。這坨代碼有點大,我還是寫注釋在代碼裡。

public void run() {
  if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
  if (mAddToBackStack) {
    if (mIndex < 0) {
      throw new IllegalStateException("addToBackStack() called after commit()");
    }
  }
  bumpBackStackNesting(1);
  TransitionState state = null;
  SparseArray<Fragment> firstOutFragments = null;
  SparseArray<Fragment> lastInFragments = null;
  if (SUPPORTS_TRANSITIONS) {
    firstOutFragments = new SparseArray<Fragment>();
    lastInFragments = new SparseArray<Fragment>();
    calculateFragments(firstOutFragments, lastInFragments);
    state = beginTransition(firstOutFragments, lastInFragments, false);
  }
  int transitionStyle = state != null ? 0 : mTransitionStyle;
  int transition = state != null ? 0 : mTransition;
  // 注意這裡要開始 while 循環了,要遍歷剛才咱們說的鏈表了。
  Op op = mHead;
  while (op != null) {
    int enterAnim = state != null ? 0 : op.enterAnim;
    int exitAnim = state != null ? 0 : op.exitAnim;
    switch (op.cmd) {
      // OP_ADD 很簡單,mManager.addFragment(f, false); 其他的幾個也類似,調用 mManager 相應的方法。
      case OP_ADD: {
        Fragment f = op.fragment;
        f.mNextAnim = enterAnim;
        mManager.addFragment(f, false);
      } break;
      case OP_REPLACE: {
        Fragment f = op.fragment;
        if (mManager.mAdded != null) {
          for (int i=0; i<mManager.mAdded.size(); i++) {
            Fragment old = mManager.mAdded.get(i);
            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                "OP_REPLACE: adding=" + f + " old=" + old);
            if (f == null || old.mContainerId == f.mContainerId) {
              if (old == f) {
                op.fragment = f = null;
              } else {
                if (op.removed == null) {
                  op.removed = new ArrayList<Fragment>();
                }
                op.removed.add(old);
                old.mNextAnim = exitAnim;
                if (mAddToBackStack) {
                  old.mBackStackNesting += 1;
                  if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                      + old + " to " + old.mBackStackNesting);
                }
                mManager.removeFragment(old, transition, transitionStyle);
              }
            }
          }
        }
        if (f != null) {
          f.mNextAnim = enterAnim;
          mManager.addFragment(f, false);
        }
      } break;
      case OP_REMOVE: {
        Fragment f = op.fragment;
        f.mNextAnim = exitAnim;
        mManager.removeFragment(f, transition, transitionStyle);
      } break;
      case OP_HIDE: {
        Fragment f = op.fragment;
        f.mNextAnim = exitAnim;
        mManager.hideFragment(f, transition, transitionStyle);
      } break;
      case OP_SHOW: {
        Fragment f = op.fragment;
        f.mNextAnim = enterAnim;
        mManager.showFragment(f, transition, transitionStyle);
      } break;
      case OP_DETACH: {
        Fragment f = op.fragment;
        f.mNextAnim = exitAnim;
        mManager.detachFragment(f, transition, transitionStyle);
      } break;
      case OP_ATTACH: {
        Fragment f = op.fragment;
        f.mNextAnim = enterAnim;
        mManager.attachFragment(f, transition, transitionStyle);
      } break;
      default: {
        throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
      }
    }
    op = op.next;
  }
  // 最後還調用了moveToState() 這個方法。跟剛才的區別,看最後一個參數,一個true,一個false。
  // 而且注意,這行代碼在 while 循環之後。
  mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
  if (mAddToBackStack) {
    mManager.addBackStackState(this);
  }
}

以上所述是小編給大家介紹的Android Fragment 和 FragmentManager 的代碼分析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved