fragment完全解读

前言

最近看Android5.0以后提供的控件,好多新特性,竟然发现我好多控件都没使用过,最近好好补一补相关知识,Fragment这个东东用的越来越多,可是总是觉得有些没有办法完全理解,最近发现真的Fragment相关代码很少,就想着自己直接分析一下源码。希望完全搞懂Fragment。我们看的源码版本是Androidx

正文

Fragment有两种使用方法:

  1. xml文件中解析
  2. 代码中调用
    xml中解析这里我不想过多分析,我们只吧目光集中到java代码中动态控制,最简单的一个使用是:
supportFragmentManager.beginTransaction().apply { 
            add(R.id.my_nav_host_fragment,Register())
            commit()
        }

这里的使用很简单,不过我们还是需要慢慢找到核心问题关键,从getSupportFragmentManager()中我们很容易定位到FragmentActivity是控制我们Fragment的核心关键,这里我就先不解释,等下让代码告诉我们
我们只用关心到底supportFragmentManager这个是如何构造的。

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

看到代码忽然觉得清晰了不少,这直接引入了一个Controller是为了分层,这里觉得是为了将来扩展。通过观察FragmentController我们很容易发现他只是对mHost的一个简单代理,更容易看到mHost也仅仅是对mFragmentManager代理罢了,这些我们以后子啊分析。我们找mHost初始化的时候,很容易发现他是经过HostCallbacks()构造函数赋值的。我们大概看下类HostCallbacks
hostCallback这里除了mFragmentManager比较重要,其他都是没啥用的东东,很明显这是用了代理然后又用了代理,这里为了扩展性搞了一大堆没用的。鬼知道google想要干嘛呢。我们只用继续盯紧我们的FragmentManagerImpl就可以了。
下面我们继续追踪到底Fragment是如何被添加到activity上的,我们把目光关注到beginTransaction()函数上。在FragmentManagerImpI 我们很容易看到

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

看名字一目了然,就是一个堆栈嘛,我们看下源码来,大概继承关系就是这样
在这里插入图片描述
我们还是直接来看add的操作:

    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment) {
        doAddOp(containerViewId, fragment, null, OP_ADD);
        return this;
    }
    private void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
      
      //判断 并且初始化一大堆Fragment的内容,其他啥也没做

        addOp(new Op(opcmd, fragment));
    }
   
    ArrayList<Op> mOps = new ArrayList<>();//操作的队列
    void addOp(Op op) {这里记录了Fragment的内容
        mOps.add(op);
        ......
    }

这个堆栈和操作信息已经搞完了,关于其他参数我久不一一介绍直接进入commit阶段吧

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

int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        mCommitted = true;
        if (mAddToBackStack) {//这里我们没有加入回退栈,所以是false
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

所以这里会很简单,就是把所有的操作记录列成一个表,交给manage,我们继续跟踪代码:

 public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        ....
        synchronized (this) {
            ......
          
          //这里又添加一个数组,相当于两级分类
            mPendingActions.add(action);
            scheduleCommit();
        }
    }
    void scheduleCommit() {
        synchronized (this) {
            ......
            //放弃所有操作,从新开始新的操作,并且线程安全。
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
           ......
        }
    }
    Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    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) {

//这是核心代码。具体操作是用BackStackState的generateOps方法,仅仅是吧自己加入records和isPop
            for (int i = 0; i < numActions; i++) {
                didSomething |= mPendingActions.get(i).generateOps(records, isPop);
            }
        return didSomething;
    }

其实这里很简单就是把FragmentManage中的操作列表mPendingActions的具体操作加入一个临时的列表records他其实就是mTmpRecords,这里就已经很清晰了(多了一个tmp仅仅是为了异步进程安全)。
这里我们数据已经准备好了,我们进入中心看到最核心的函数removeRedundantOperationsAndExecute()

    /**
     * Remove redundant BackStackRecord operations and executes them. This method merges operations
     * of proximate records that allow reordering. See
     * {@link FragmentTransaction#setReorderingAllowed(boolean)}.
     * <p>
     * For example, a transaction that adds to the back stack and then another that pops that
     * back stack record will be optimized to remove the unnecessary operation.
     * <p>
     * Likewise, two transactions committed that are executed at the same time will be optimized
     * to remove the redundant operations as well as two pop operations executed together.
     **/
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop) {
        ......

        // Force start of any postponed transactions that interact with scheduled transactions:
       //处理影响当前的操作,这个看下源码大概可以看到对我们操作场景没太多影响。
        executePostponedTransaction(records, isRecordPop);

		.......
                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
        ......       
    }

看懂注释的就容易理解,这里是可以合并操作,但是这里有个比较复杂的循环不过我们稍微分析一下,主要是应付异步线程安全和多操作同时处理的问题,这里我们主要关注一个函数executeOpsTogether

/**
     * Executes a subset of a list of BackStackRecords, all of which either allow reordering or
     * do not allow ordering.
     */
    private void executeOpsTogether(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
        
       oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
            
        executeOps(records, isRecordPop, startIndex, endIndex);
        .....
    }

这个函数相当复杂但是主要也就这两个函数调用关键。第一个只是取出我们刚刚新建的fragment,最后一个才是真的吧fragment加入到页面上。我们继续阅读executeOps

private static void executeOps(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
       
                record.executeOps();
    }
        /**
     * Executes the operations contained within this transaction. The Fragment states will only
     * be modified if optimizations are not allowed.
     */
    void executeOps() {
        final int numOps = mOps.size();
        for (int opNum = 0; opNum < numOps; opNum++) {
          
            switch (op.cmd) {
                case OP_ADD:
                    f.setNextAnim(op.enterAnim);
                    mManager.addFragment(f, false);
                    break;
                case OP_REMOVE:
                    f.setNextAnim(op.exitAnim);
                    mManager.removeFragment(f);
                    break;
                case OP_HIDE:
                    f.setNextAnim(op.exitAnim);
                    mManager.hideFragment(f);
                    break;
                case OP_SHOW:
                    f.setNextAnim(op.enterAnim);
                    mManager.showFragment(f);
                    break;
                case OP_DETACH:
                    f.setNextAnim(op.exitAnim);
                    mManager.detachFragment(f);
                    break;
                case OP_ATTACH:
                    f.setNextAnim(op.enterAnim);
                    mManager.attachFragment(f);
                    break;
                case OP_SET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(f);
                    break;
                case OP_UNSET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(null);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
            if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
                mManager.moveFragmentToExpectedState(f);
            }
        }

    }

这离又回到了真的栈的操作 我们继续研究,这里又回到了manage中了 我们现在只关注add,所以函数也变得比较简单我们继续看

public void addFragment(Fragment fragment, boolean moveToStateNow) {
        
        ......
                moveToState(fragment);
           ......
    }

emmm貌似变得简单起来了他只是改变下fragment的状态,不过真的添加到页面和声明周期我们应该已经到来了。我们继续观察

if (newState > Fragment.INITIALIZING) {
	......

	f.mHost = mHost;
	f.mParentFragment = mParent;

//看名字就知道是准备Attached我们基本计入声明周期了
	dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
	f.mCalled = false;
	//已经喀什onattach了
	f.onAttach(mHost.getContext());
	
	//有是声明周期
	dispatchOnFragmentAttached(f, mHost.getContext(), false);

//这里就到了创造和恢复的问题了,其实都是调用了oncreat了
	if (!f.mIsCreated) {
		dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);
		f.performCreate(f.mSavedFragmentState);//函数中又一次改变了state的装态
		dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
	} else {
		f.restoreChildFragmentState(f.mSavedFragmentState);
		f.mState = Fragment.CREATED;
	}
	f.mRetaining = false;
}
case Fragment.CREATED:
// This is outside the if statement below on purpose; we want this to run
// even if we do a moveToState from CREATED => *, CREATED => CREATED, and
// * => CREATED as part of the case fallthrough above.
ensureInflatedFragmentView(f);

if (newState > Fragment.CREATED) {
	
		f.mContainer = container;
		//fragment的oncreatview函数
		f.performCreateView(f.performGetLayoutInflater(
				f.mSavedFragmentState), container, f.mSavedFragmentState);
		//emmmm 原来就是这里
				container.addView(f.mView);
			}
			//有事生命周期
			f.onViewCreated(f.mView, f.mSavedFragmentState);
			dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
					false);

	}

	f.performActivityCreated(f.mSavedFragmentState);
	//数据恢复的东东
	if (f.mView != null) {
		f.restoreViewState(f.mSavedFragmentState);
	}
	f.mSavedFragmentState = null;
}

这里选了两个fragment装态来表示一下fragment的添加过程,其实就相当简单,就是通过oncreatview构造view 然后加入视图。这里基本上就追踪完了fragment整个添加流程,以后有空再重新整理一下,理清楚到底异步是如何做到线程安全的。

后记

花了两天时间,大概了解一下fragment到底是个什么,觉得对这个奇奇怪怪的fragment的有了一定的认识,下次在跟踪activity生命周期分析fragment的生命周期继续整理fragment

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值