Fragment 知識梳理(1) Fragment 源碼解析

1、概述

官方是從3.0開始引入Fragment的,在文檔中有對於其使用的詳細介紹,能夠看出來,在它剛出現時你們對於它是十分推崇的。然而隨着使用Fragment開發的項目愈來愈多,在一些複雜場景下逐漸暴露出了它的一些問題,對因而否繼續使用Fragment你們也有不一樣的見解。目前在項目中也有大量以前留下來的使用 Fragment的代碼, monkey有時會跑出一些莫名奇妙的問題,在這裏咱們對於使用Fragment的好壞先不作評價,而是看看它內部整個的實現過程,學習它的思想,在之後出現問題的時候也方便排查,同時你們能夠根據本身的需求來決定是否使用Fragmentjava

2、Fragment事務的執行過程

在操做Fragment時,第一件是就是經過ActivitygetFragmentManger()方法獲得一個FragmentManager對象:bash

Fragment manager = getFragmentManager();
複製代碼

咱們看一下Activity.java中的這個方法:ide

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

public FragmentManager getFragmentManager() {
    return mFragments.getFragmentManager();
}

class HostCallbacks extends FragmentHostCallback<Activity> {
    public HostCallbacks() {
        super(Activity.this);
    }
}
複製代碼

能夠看到getFragmentManager()調用的是FragmentController的接口,那麼咱們再看看這個類對應的方法:oop

private final FragmentHostCallback<?> mHost;

public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
   return new FragmentController(callbacks)
}

private FragmentController(FragmentHostCallback<?> callbacks) {
    mHost = callbacks;
}

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

原來FragmentController是把一個callback給包裝了起來,真正完成任務的是FragmentHostCallback佈局

final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();

FragmentManagerImpl getFragmentManagerImpl() {
    return mFragmentManagerImpl;
}
複製代碼

那麼結論就是咱們實際獲得的是Activity#mFragments#mHost#mFragmentManagerImpl這個變量,獲得這個實例以後,咱們經過它的beginTransition方法獲得一個事務:post

FragmentTransation transation = manager.beginTransation();
複製代碼

咱們看一下這個FragmentTransation到底是個什麼東西,FragmentManagerImplFragmentManager的一個內部類,它實現了該接口:學習

final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {

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

}
複製代碼

原來這個FragmentTransation也是一個接口,它的實現是BackStackRecord,而且在每次 beginTransaction時都是返回一個新的事務對象,包括以後進行的後退操做都是經過這個事務對象來管理的,這個對象中保存了建立它的FragmentManagerImpl實例。 BackStackRecord實際上是BackStackState的一個內部類,咱們平時就是經過它來進行add、remove、replace、attach、detach、hide、show等操做的,進入源碼看一下這些操做背後都幹了些什麼:ui

final class BackStackRecord extends FragmentTransation implements FragmentManager.BackStackEntry, Runnable {

public FragmentTransation add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}

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

public FragmentTransation add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
}

public FragmentTransation replace(int containerViewId, Fragment fragment) {
    return replace(containerViewId, fragment, null);
}

public FragmentTransation replace(int containerViewId, Fragment fragment, String tag) {
    if (containerViewId == 0) {
        //replace操做必需要指定containerViewId.
   }
   doAddOp(containerViewId, fragment, tag, OP_REPLACE);
}

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

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

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

public FragmentTransaction attach(Fragment fragment) {
   Op op = new Op();
   op.cmd = OP_ATTACH;
   op.fragment = fragment;
   addOp(op);
}

public FragmentTransaction detach(Fragment fragment) {
   Op op = new Op();
   op.cmd = OP_DETACH;
   op.fragment = fragment;
   addOp(op);
}

//新建一個操做,並給操做賦值。
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)) {
             //若是這個 fragment 以前已經有了tag,那麼是不容許改變它的。
      }
      fragment.mTag = tag;
   }
   if (containerViewId != 0) {
        if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
            //若是這個fragment已經有了mFragmentId,那麼不容許改變它。
       }
       fragment.mContainerId = fragment.mFragmentId = containerViewId;
   }
   Op op = new Op();
   op.cmd = opcmd;
   op.fragment = fragment;
   addOp(op);
}
//把操做添加到鏈表之中。
void addOp() {
    if (mHead == null) {
        mHead = mTail = op;
    } else {
       op.prev = mTail;
       mTail.next = op;
       mTail = op;
    }
    mNumOp++;
}
}
複製代碼

看完上面這段代碼,咱們能夠獲得下面這些信息:this

  • 咱們調用這些操做以後僅僅是把這個操做添加到了BackStackRecord當中的一個鏈表。
  • 在進行attach、detach、hide、show、remove這五個操做時是不須要傳入containerViewId的,由於在執行這些操做以前這個Fragment必然已經通過了add操做,它的containerId是肯定的。
  • 在執行add操做時,須要保證FragmentmTagmContainerViewId在不爲空時(也就是這個 Fragment實例以前已經執行過add操做),它們和新傳入的tagcontainerViewId必須是相同的,這是由於在FragmentManagermActive列表中保存了全部被添加進去的Fragment,而其提供的 findFragmentById/Tag正是經過這兩個字段做爲判斷的標準,所以不容許同一個實例在列表當中重複出現兩次。
  • replace操做和add相似,它增長了一個額外條件,就是containerViewId不爲空,由於replace須要知道它是對哪一個container進行操做,後面咱們會看到replace實際上是一個先removeadd的過程,所以`add 的那些判斷條件一樣適用。

那麼這個鏈表中的操做何時被執行呢,看一下commit()操做:spa

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

int commitInternal(boolean allowStateLoss) {
    if (mCommitted) {
        //已經有事務處理,拋出異常。
    }
    mCommited = true;
    if (mAddToBackState) {
         mIndex = mManager.allocBackStackIndex(this);
    } else {
         mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);
}
複製代碼

它最後調用了FragmentManagerenqueueAction方法,咱們進去看一下里面作了什麼:

public void enqueueAction(Runnable action, boolean allowStateLoss) {    
    if (!allowStateLoss) {        
        checkStateLoss();    
    }    
    synchronized (this) {        
        if (mDestroyed || mHost == null) {            
           //若是Activity已經被銷燬,那麼拋出異常。          
        }       
        if (mPendingActions == null) {            
            mPendingActions = new ArrayList<Runnable>();        
        }
        //由於BackStackRecord實現了Runnable接口,把加入到其中。        
        mPendingActions.add(action);        
        if (mPendingActions.size() == 1) {        
           mHost.getHandler().removeCallbacks(mExecCommit);            
           mHost.getHandler().post(mExecCommit);        
        }   
    }
}

Runnable mExecCommit = new Runnable {
    public void run() {
        execPendingActions();
    }
}

public boolean execPendingActions() {
   if (mExecutingActions) {
      //已經有操做在執行,拋出異常
   }
   if (Looper.myLooper() != mHost.getHandler().getLooper()) {
       //不在主線程中執行,拋出異常
   }
   boolean didSomething = false;
   while (true) {
      int numActions;
      sychronized(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++) {
            //好吧,這裏就又回調了BackStackRecord的run()方法
            mTmpActions[i].run();
            mTmpActions[i] = null;
       }
       mExecutingActions = false;
       didSomething = true; 
   }
}
複製代碼
  • 在調用commit以後,把BackStackRecord加入到FragmentManagerImplmPendingActions中,並且經過查看FragmentManager的源碼也能夠發現,全部對mPendingActions的添加操做只有這一個地方調用,
  • 當其大小爲1時,會經過主線程的Handler post一個Runnable mExecCommit出去,當這個Runnable執行時調用execPendingActions()方法。
  • execPendingActions它會拿出這個mPendingActions當中的全部Runnable執行(若是它是 BackStackRecord調用過來的,那麼就是調用BackStackRecordrun方法),並把這個列表清空。在每一個BackStackRecordrun方法執行時,它是經過遍歷BackStackRecord鏈表當中每一個節點的cmd來判斷咱們以前經過FragmentTransation加入指望執行的那些操做的。
  • 能夠看出execPendingActions這個方法很關鍵,由於它決定了咱們添加的操做何時會被執行,咱們看下還有那些地方調用到了它,爲何要分析這個呢,由於咱們在項目當中發現不少來自於Fragment的異常都是由咱們後面談論的moveToState方法拋出的,而moveToState執行的緣由是execPendingActions被調用了,所以瞭解它被調用的時機是咱們追蹤問題的關鍵,關於調用的時機,咱們都總結在下面的註釋當中了:
<!-- Activity.java -->
final void performStart() {
    mFragments.execPendingActions(); //有可能在 Activity 的 onStart 方法執行前
    mInstrumentation.callActivityOnStart(this);
}

final void performResume() {
    performRestart();
    mFragments.execPendingActions(); //若是是從 Stopped 過來的,那麼有可能在 onStart 到 onResume 之間。
    ....
    mInstrumentation.callActivityOnResume(this);
    ....
    mFragments.dispatchResume(); 
    mFragments.execPendingActions(); //有可能在 onResume 到 onPause 之間。
}

<!-- FragmentManager.java -->
public void dispatchDestroy() { //這個調用在 onDestroy 以前。
    execPendingActions();
}

public boolean popBackStackImmediate() {
    executePendingTransactions();
}

public boolean popBackStackImmediate(String name, int flags) {
    executePendingTransactions();
}

複製代碼

關於FragmentManager的討論咱們先暫時放一放,看一下BackStackRecord是怎麼執行鏈表內部的操做的:

public void run() {
    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) {
                    //遍歷 mAdded列表,找到 containerId 相同的 old Fragment.
                    if (old == f) {
                        op.fragment = f = null;
                    } else {
                        if (op.removed == null) {
                             op.removed = new ArrayList<Fragment>();
                        }
                        op.removed.add(old); //這裏要把replace以前的記下來是爲了後退棧準備的。
                        if (mAddToBackStack) {
                             old.mBackStackNesting += 1;
                        }
                        mManager.removeFragment(old, transition, transitionStyle);
                    }
                }
                if (f != null) { 
                     f.addFragment(f, false);
                }
                break;
                //後面的remove,hide,show,attach,detach就是調用了FragmentManager中相應的方法,沒什麼特別的,就不貼出來了
                case xxx:
                   
            }
        }
        op = op.next;
    }
    //mCurState此時爲FragmentManager當前的狀態,其他的參數不用管。    
    mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true); 
    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    } 
}
複製代碼

咱們來看一下FragmentManagerImpl對應的addFragment等操做:

public void addFragment(Fragment fragment, boolean moveToStateNow) {
   makeActive(fragment); //加入到mActive列表中。
   if (!fragment.mDetached) {
       if (mAdd.contains(fragment)) {
           //已經在mAdded列表,拋出異常。
       }
       mAdded.add(fragment);
       fragment.mAdded = true;
       fragment.mRemoving = false;
       if (moveToStateNow) {
           moveToState(fragment);
       }
   }
}

public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
    final boolean inactive = !fragment.isInBackStack();
    if (!fragment.mDetach || inactive) {
        if (mAdded != null) {
            mAdded.remove(fragment); //從mAdded列表中移除。
        }
        fragment.mAdded = false;
        fragment.mRemoving = true;
        //這裏會根據是否加入後退棧來判斷新的狀態,最後會影響到Fragment生命週期的調用,若是是沒有加入後退棧的,那麼會多調用onDestroy、onDetach方法。
        moveToState(fragment, inactive ? Fragment.INITIALZING : Fragment.CREATED, transition, transitionStyle, false); 
    }
}

public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
    if (!fragment.mHidden) {
       fragment.mHidden = true;
       if (fragment.mView != null) {
          fragment.mView.setVisibility(View.GONE);
       }
    }
    fragment.onHiddenChanged(true);
}

public void showFragment(Fragment fragment, int transition, int transitionStyle) {
    if (fragment.mHidden) {
        fragment.mHidden = false;
        if (fragment.mView != null) {
             fragment.mView.setVisibility(View.VISIBLE);
        }
        fragment.onHiddenChanged(false);
    }
}

public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
    if (!fragment.mDetached) {
        fragment.mDetached = true;
        if (fragment.mAdded) {
             if (mAdded != null) {
                 mAdded.remove(fragment);
             }
             fragment.mAdded = false;
             moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
        }
    }
}

public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
    if (fragment.mDetached) {
         if (!fragment.mAdded) {
              if (mAdded.contains(fragment)) {
                  //mAdded列表中已經有,拋出異常,
              }
              mAdded.add(fragment);
              fragment.mAdded = true;
              moveToState(fragment, mCurState, transition, transitionStyle, false);
         }
    }

}
複製代碼

這裏的操做不少,咱們須要明白如下幾點:

  • mActivemAdded的區別:mActive表示執行過add操做,而且其沒有被移除(移除表示的是在不加入後退棧的狀況下被removeFragment),全部被動改變Fragment狀態的調用都是遍歷這個列表;而 mAdded則表示執行過add操做,而且沒有執行detachFragment/removeFragment,也就是說Fragment 是存在容器當中的,可是有多是被隱藏的(hideFragment)。
  • attachFragment 必須保證其狀態是 mDetach 的,而該屬性的默認值是 false,只有在執行過 detach 方法後,才能執行,執行它會把 f.mView 從新加入到 container 中。
  • detachFragment 會把 f.mViewcontainer 中移除。
  • removeFragmentdetachFragment 會強制改變 Fragment 的狀態,這是由於它們須要改變 Fragment 在佈局中的位置,而這經過被動地接收 FragmentManager 狀態(即所在Activity的狀態)是沒法實現的。在 removeFragment 時,會根據是否加入後退棧來區分,若是假如了後退棧,由於有可能以後會回退,而回退時遍歷的是 mActive 列表,若是把它的狀態置爲Fragment.INITIALZING,那麼在 moveToState方法中就會走到最後一步,把它從mActive列表中移除,就找不到了也就沒法恢復,所以這種狀況下Fragment最終的狀態的和detachFragment是相同的。
  • 在加入後退棧時,detachFragment 時,會把 mDetach置爲true,這種狀況下以後能夠執行 attachFragment操做但不能執行 addFragment操做;removeFragment 以後能夠執行 addFragment操做但不能執行 attachFragment操做。
  • showFragmenthideFragment 並不會主動改變 Fragment 的狀態,它僅僅是回調 onHiddenChanged方法,其狀態仍是跟着 FragmentManager 來走。
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true); 
複製代碼

這個方法纔是 Fragment的核心,它的思想就是根據 Fragment 指望進入的狀態和以前的狀態進行對比,從而調用 Fragment相應的生命週期,那麼問題就來,什麼是指望進入的狀態呢,我認爲能夠這麼理解:

  • 用戶沒有進行主動操做,可是 FragmentFragmentManager的狀態不一致,這時須要發生變化。
  • 用戶進行了主動操做,不管FragmentFragmentManager的狀態是否一致,由於 Fragment 的狀態發生了變化,所以這時也須要執行。

這裏咱們爲了簡便起見先看一下和生命週期有關的代碼:

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.

void moveToState(int newState, int transit, int transitStyle, boolean always) {
   if (!always && mCurState == newState) {
      return;
   }
   mCurState = newState;
   if (mActive != null) {
       for (int i = 0; i < mActive.size; i++) {
           //在addFragment中經過makeActive加入進去
           Fragment f = mActive.get(i);
           moveToState(f, newState, transit, transitStyle, false);
       }
   }
}

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
    if (f.mState < newState) { //新的狀態高於當前狀態
        switch(f.mState) {
             case Fragment.INITIALZING:
                 f.onAttach(mHost.getContext());
                 if (f.mParentFragment == null) {
                     mHost.onAttachFragment(f);
                 }
                 if (!f.mRetaining) {
                     f.performCreate(f.mSavedFragmentState);
                 }
                 if (f.mFromLayout) {
                     f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), null, f.mSavedFragmentState);
                     if (f.mHidden) f.mView.setVisibility(View.GONE);
                     f.onViewCreated(f.mView, f.mSavedFragmentState);
                 }
             case Fragment.CREATED:
                  if (newState > Fragment.CREATED) {
                      if (!f.mFromLayout) {
                          ViewGroup container = null;
                          if (f.mCotainerId != null) {
                               cotainer = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                               if (container == null && !f.mRestored) {
                                  //no view found
                               }
                          }
                          f.mContainer = container;
                          f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), null, f.mSavedFragmentState); 
                          if (f.mView != null) {
                               f.mInnerView = f.mView;
                               if (Build.VERSION.SDK >= 11) {
                                   ViewCompact.setSaveFromParentEnable(f.mView, false);
                               } else {
                                   f.mView = NoSaveStateFrameLayout.wap(f.mView);
                               }
                               if (f.mHidden) f.mView.setVisibility(View.GONE);
                               f.onViewCreated(f.mView, f.mSavedFragmentState);
                          } else {
                               f.mInnerView = null;
                          }
                      }
                      f.performActivityCreated(f.mSavedFragmentState);
                  }
             case Fragment.ACTIVITY_CRREATED:
             case Fragment.STOPPED:
                 if (newState > Fragment.STOPPED) {
                      f.performStart();
                 }
             case Fragment.STARTED:
                 if (newState > Fragment.STARTED) {
                      f.performResume();
                 }
        }
    } else if (f.mState > newState) { //新的狀態低於當前狀態
        switch(f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    f.performPause();
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    f.performStop();
                }
            case Fragment.STOPPED::
                if (newState < Fragment.STOPPED) {
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    f.performDestroyView(); //調用onDestory()
                    if (f.mView != null && f.mContainer != null) {
                        f.mContainer.removeView(f.mView); //把Fragment的View從視圖中移除。
                   }
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                      if (f.mAnimationAway != null) {
                          ...
                      } else {
                           if (!f.mRetaining) {
                               f.performDestory();
                           } else {
                              f.mState = Fragment.INITIALIZING;
                           }
                           f.onDetach();
                           if (!f.mRetaining) {
                               makeInActive(f);//把Fragment從mActive中移除,並把Fragment的全部狀態恢復成初始狀態,至關於它是一個全新的Fragment。
                           } else {
                               f.mHost = null;
                               f.mParentFragment = null;
                               f.mFragmentManager = null;
                               f.mChildFragmentManager = null;
                           }
                      }
                }
        }
}
複製代碼

moveToState方法中,咱們看到了許多熟悉的面孔,就是咱們平時最常談到的 Fragment 的生命週期,經過這段代碼咱們就能夠對它的生命週期有更加直觀的理解。

相關文章
相關標籤/搜索