FragmentManager详解

来源:互联网 发布:淘宝吸黑头仪器有用吗 编辑:程序博客网 时间:2024/06/08 09:26
       很久没去用fragment了,渐渐的开始忘记了。今天恰好遇见同事使用fragment,导致程序发生崩溃,因此我重新对fragment进行了分析。我会通过我解决问题的方式,将我理解fragment的思路,告诉大家。
     
     首先,我先按照自己的理解,写了这样一段代码,用于fragment切换。
    
FragmentManager mManager = getSupportFragmentManager();FragmentTransaction transaction = manager.beginTransaction();   Fragment fragment = manager.findFragmentByTag(tag);if(fragment!=null){ transaction.attach(fragment);}else{ transaction.add(R.id.container,fragment,tag);}transaction.commit();
      上面这段代码,参考了一些资料,自己对这段代码还是抱有一些疑问:

1.FragmentManager是什么?
2.beginTransaction(),如何得到FragmentTransaction的?
3.FragmentTransaction 的attach  add这些方法,都是做了什么操作?

     然后,我就带着这些疑问,开始翻源码了。
     首先,我们先查看FragmentManager:

FragmentManager
    
public abstract class FragmentManager 

      FragmentManager是抽象类,定义了很多方法。具体的实现,在于它的内部类
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory
因此,FragmentManager的操作,大部分都在FragmentManagerImpl中。
      我们先看beginTransaction()方法:

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

      FragmentTransaction其实是BackStackRecord的实例,我猜想,BackStackRecord是FragmentTransaction的子类,可以肯定,FragmentTransaction的操作,实际上是BackStackRecord的实现的。
不要着急,后面将会分析BackStackRecord。
      然后,我们看findFragmentByTag():
 
  public Fragment findFragmentByTag(String tag) {        if (mAdded != null && tag != null) {            // First look through added fragments.            for (int i=mAdded.size()-1; i>=0; i--) {                Fragment f = mAdded.get(i);                if (f != null && tag.equals(f.mTag)) {                    return f;                }            }        }        if (mActive != null && tag != null) {            // Now for any known fragment.            for (int i=mActive.size()-1; i>=0; i--) {                Fragment f = mActive.get(i);                if (f != null && tag.equals(f.mTag)) {                    return f;                }            }        }        return null;    }

      mAdded和mActive,是两个ArrayList<Fragment>,它们的作用,分别是来保存通过transaction.add和所有的fragment,在这个方法中,先对mAdded进行查找,没有查找到,再到mActive中查找,查找tag对应的fragment。如果存在,我们将调用FragmentTransaction的attach方法。
       接下来,我们分析这个attach方法。前面说到,FragmentTransaction实例,是有BackStackRecord实例化得到的。因此,我们直接去BackStackRecord查看attach方法,可以发现,BackStackRecord的方法,基本和FragmentTransaction的方法一致。打开attach方法:
      
 public FragmentTransaction attach(Fragment fragment) {        Op op = new Op();        op.cmd = OP_ATTACH;        op.fragment = fragment;        addOp(op);        return this;    }
      Op是什么呢?
    
 static final class Op {        Op next;        Op prev;        int cmd;        Fragment fragment;        int enterAnim;        int exitAnim;        int popEnterAnim;        int popExitAnim;        ArrayList<Fragment> removed;    }
      Op是静态内部类,通过命名,我们可以知道,BackStackRecord其实是通过链表实现的栈。Op是节点,保存了Fragment的相关信息。    最后,attach方法在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++;    }
      
       添加一个节点,数量++;
     
     最后一个方法,commit().
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);        }        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }
    可以看到,commit最终调用的是mManager.enqueueAction(this, allowStateLoss);转来转去,最后还是回到了FragmentManager的方法,那我们就看看enqueueAction这个方法吧。
      这个方法名称,就是把动作放到队列里面去,事实上,每个BackStackRecord相当于一个动作,就是每次beginTransaction,其实就是在构建一次动作,最后commit就是添加到队列里,然后得到执行。

        到最后,执行动作的类,是FragmentManager的execPendingActions方法。

       
   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;        }                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;    }
        看到Looper,我们应该知道动作的处理方式了。然后,后面是个while的死循环,mTmpActions[i].run();是方法的核心,它的实现来自BackStackRecord。为什么这么说,因为BackStackRecord是通过enqueueAction添加到list中,最后到了mTmpActions中,所以,mTmpActions[i].run();调用的还是BackStackRecord的run(),我们按照预期的想法,确实在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;        Op op = mHead;        while (op != null) {            int enterAnim = state != null ? 0 : op.enterAnim;            int exitAnim = state != null ? 0 : op.exitAnim;            switch (op.cmd) {                case OP_ADD: {                    Fragment f = op.fragment;                    f.mNextAnim = enterAnim;                    mManager.addFragment(f, false);                } break;                case OP_REPLACE: {                    Fragment f = op.fragment;                    int containerId = f.mContainerId;                    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 (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 = 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;        }        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);        if (mAddToBackStack) {            mManager.addBackStackState(this);        }    }

      结论就出来了,其实FragmentTransaction调用的方法,还是FragmentManagerImpl的方法。通过上面,一系列的过程,我们发现,通过这样的复杂的设计,才保证了Fragment的调用的正确性。大家,通过我翻查源码的过程,应该也理解了FragmentManager的设计了。是不是很有帮助呢?

       


2 1
原创粉丝点击