前言
最近看Android5.0以后提供的控件,好多新特性,竟然发现我好多控件都没使用过,最近好好补一补相关知识,Fragment这个东东用的越来越多,可是总是觉得有些没有办法完全理解,最近发现真的Fragment相关代码很少,就想着自己直接分析一下源码。希望完全搞懂Fragment。我们看的源码版本是Androidx
正文
Fragment有两种使用方法:
- xml文件中解析
- 代码中调用
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
这里除了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