Fragment源码简析(一)

先看使用Fragment的简单例子,这里用Android support v4的源码:

Fragment fragment= new Fragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.container,fragment).commit();

进入FragmentActivity的getSupportFragmentManager方法:

public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
}
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

可以看到FragmentManager是从FragmentController里取得的,那么看看FragmentController里有什么:

public class FragmentController {
     public static final FragmentController 
         createController(FragmentHostCallback<?> callbacks) {
           return new FragmentController(callbacks);
     }
      public FragmentManager getSupportFragmentManager() {
           return mHost.getFragmentManagerImpl();
     }
     .........
}

又来了新类FragmentHostCallback,里面有个mFragmentManager变量:

public abstract class FragmentHostCallback<E> extends FragmentContainer {
       final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
       .........
}

看到这里,只要知道FragmentManager最后来自于FragmentManagerImpl就可以了,看名字知道是FragmentManager的实现类。
下面进入beginTransaction方法,在FragmentManagerImpl里找到:

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

beginTransaction的作用就是返回一个BackStackRecord对象,可见事务办理都是交给了BackStackRecord这个类,这个类值得注意。
继续往下走,例子只是进行了add这个事务。查看BackStackRecord类里的add方法,有几个重载方法,最后都走到了其中一个,有空可以查看所有的源码,这里的主干代码是:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;
        fragment.mTag = tag;
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
        addOp(new Op(opcmd, fragment));//这里的opcmd是OP_ADD
    }

只是设置了fragment的成员变量,然后调用addOp,注意opcmd,对fragment的操作有很多,这里是OP_ADD,常用的还有OP_REPLACE,OP_REMOVE,OP_HIDE,OP_SHOW等。addOp方法干了啥:

void addOp(Op op) {
        mOps.add(op);
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
}
ArrayList<Op> mOps = new ArrayList<>();

动画相关的不管,这步操作只是简单的将封装的ADD事务插入到ArrayList mOps里。现在准备工作做好了,到了最后一步,提交事务,调用commit()方法:

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

这里流程只关注最后一句,由FragmentManager将之前的ADD操作加入队列。
一句话总结下上面的流程:将新建Fragment的事务封装成ADD操作交给FragmentManager,FragmentManager将ADD操作入队列。
下面看看入队列做了什么:

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {//PendingActions 是空,就新建一个
                mPendingActions = new ArrayList<>();
            }
            mPendingActions.add(action);//ADD操作加入ArrayList
            scheduleCommit();
        }
    }

enqueue意思是入队,但是这里其实是个列表,不用在意这些细节。加入队列,后面scheduleCommit的名字看起来就是提交这个列表,执行列表里的各个操作了,看看源码:

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

又是通过Handler机制传递消息的。Handler从哪来的?mHost是什么?最开始的FragmentActivity里有个mHandler:

public class FragmentActivity extends BaseFragmentActivityApi16 implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
         final FragmentController mFragments = 
         FragmentController.createController(new HostCallbacks());//mHost就是HostCallbacks,是FragmentHostCallback子类  
         final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
             ............
            }
         }

        };
         ......
}

//activity.mHandler,Handler在这里
public abstract class FragmentHostCallback<E> extends FragmentContainer {
   FragmentHostCallback(FragmentActivity activity) {
        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);//mHandler就是FragmentActivity里的Handler
    }
    .........
}

好了,这里我们知道了mHost.getHandler().post(mExecCommit)的Handler是来自于FragmentActivity。这里有个问题scheduleCommit是在FragmentManager里调用的,FragmentManager的mHost为什么就是FragmentController的mHost呢?之后再分析,先看主流程,继续上面的:

   mHost.getHandler().post(mExecCommit);//mExecCommit是个Runnable
   Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    /**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        ensureExecReady(true);

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

        doPendingDeferredStart();
        burpActive();

        return didSomething;
    }

private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isPop) {
        boolean didSomething = false;
        synchronized (this) {
            if (mPendingActions == null || mPendingActions.size() == 0) {
                return false;
            }

            final int numActions = mPendingActions.size();
            for (int i = 0; i < numActions; i++) {
                didSomething |= mPendingActions.get(i).generateOps(records, isPop);//开始跑OP了
            }
            mPendingActions.clear();
            mHost.getHandler().removeCallbacks(mExecCommit);
        }
        return didSomething;
    }

这里mPendingActions.get(i).generateOps(records, isPop)的generateOps是个接口方法,找到mPendingActions列表中的元素是哪个类,看看generateOps具体实现,这里的mPendingActions.get(i)就是上面的ADD操作,是个BackStackRecord对象,所以看看BackStackRecord的generateOps方法:

/**
     * Implementation of {@link FragmentManagerImpl.OpGenerator}.
     * This operation is added to the list of pending actions during {@link #commit()}, and
     * will be executed on the UI thread to run this FragmentTransaction.
     *
     * @param records Modified to add this BackStackRecord
     * @param isRecordPop Modified to add a false (this isn't a pop)
     * @return true always because the records and isRecordPop will always be changed
     */
    @Override
final class BackStackState implements Parcelable {
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        records.add(this);//ADD加入records
        isRecordPop.add(false);
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
        return true;
    }
    ...........
}

看到这里知道了,generateOpsForPendingActions只是将mPendingActions里的元素加入到了mTmpRecords。那真正执行操作的应该就是removeRedundantOperationsAndExecute方法了:

//只贴主要代码
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop) {
       .............
      final int numRecords = records.size();
      int startIndex = 0;
      for (int recordNum = 0; recordNum < numRecords; recordNum++) {
          if (startIndex != recordNum) {
                 executeOpsTogether(records, isRecordPop, startIndex, recordNum);
           }
       }
       .............
}

这里面又有很多调用,但是最终会回到每个BackStackRecord的executeOps()方法,executeOps就是个按操作类型执行各种具体动作的方法,比如OP_ADD,OP_REMOVE等等,看看OP_ADD操作干了什么:

   void executeOps() {
          final Fragment f = op.fragment;
           switch (op.cmd) {
                case OP_ADD:
                    f.setNextAnim(op.enterAnim);
                    mManager.addFragment(f, false); //第二个参数false,又回到了FragmentManager
                    break;
               .............
              }
          if (!mReorderingAllowed) {
           // Added fragments are added at the end to comply with prior behavior.
            mManager.moveToState(mManager.mCurState, true);
        }
    }     
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
    final ArrayList<Fragment> mAdded = new ArrayList<>();
     public void addFragment(Fragment fragment, boolean moveToStateNow) 
     {
        if (DEBUG) Log.v(TAG, "add: " + fragment);
        makeActive(fragment);
        if (!fragment.mDetached) {
            if (mAdded.contains(fragment)) {
                throw new IllegalStateException("Fragment already added: " + fragment);
            }
            synchronized (mAdded) {
                mAdded.add(fragment);
            }
            fragment.mAdded = true;
            fragment.mRemoving = false;
            if (fragment.mView == null) {
                fragment.mHiddenChanged = false;
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            if (moveToStateNow) {//前面传来的是false,注意
                moveToState(fragment);
            }
        }
    }
    //具体的Fragment操作都在FragmentManager里
    public void removeFragment(Fragment fragment) {
     ..........
    }
    public void hideFragment(Fragment fragment) {
       ........
    }
}

原来FragmentManager不过是将Fragment加入到自身的mAdded列表或者操作Fragment自身的属性,比如隐藏属性mHiddenChanged,具体查看其他几个方法就知道了。

再总结下上面一堆代码:将ADD操作加入FragmentManager的mPendingActions列表后提交,经过Handler机制在主线程中,执行ADD操作的executeOps方法,将新的Fragment插入到FragmentManager的mAdded列表。

走到这里,准备工作都做好了,下面该Fragment的生命周期上场了。因为传到addFragment的moveToStateNow是false,所以addFragment方法本身并没有走生命周期流程,回到executeOps方法,看到在最后调用了 mManager.moveToState。

下一篇Fragment源码简析(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350