Fragment的add,show,hide一定要一起用吗?
问题
两个fragment, fragmentTest1和fragmentTest2, 初始化fragmentTest1, 代码如下:
supportFragmentManager.beginTransaction()
.add(R.id.content, fragmentTest1, FragmentTest1::class.java.simpleName)
.commit()
然后在一个点击事件跳转到fragmentTest2, 代码如下:
val fragmentTest2 = FragmentTest2()
supportFragmentManager.beginTransaction().apply {
add(R.id.content, fragmentTest2,"FragmentTest2")
.show(fragmentTest2)
.addToBackStack(null)
.commit()
}
很明显跳转没有加hide(fragmentTest1), 那么这里会发生什么问题呢?
我们打印一下他们的生命周期,如下:
fragmentTest1: onViewCreated
fragmentTest1: onResume
fragmentTest2: onViewCreated
fragmentTest2: onResume
为啥fragmentTest1仍然可见, 问题出现在这了, 我们只能乖乖的加上hide(fragment1),逻辑才正常. 这是为什么呢.
加上hide之后, 需要重写onHiddenChanged来监听, 因为hide操作不会回调onPause
源码分析
分析一下add,shou,hide到底做了些什么操作导致onHiddenChanged的回调.
commit
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
//省略
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
//后面的跳转省略, 给出跳转的方法, 主要是事务抛到fragment的handler去执行
scheduleCommit();
到
mHost.getHandler().post(mExecCommit);
mExecCommit的定义如下:
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
execPendingActions
public boolean execPendingActions() {
ensureExecReady(true);
//后面省略
}
private void ensureExecReady(boolean allowStateLoss) {
//...
try {
executePostponedTransaction(null, null);
} finally {
mExecutingActions = false;
}
}
private void executePostponedTransaction(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop) {
//遍历mPostponedTransactions
//
listener.completeTransaction();
}
completeTransaction
public void completeTransaction() {
//省略标记
mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
}
终于到达完成事务的地方
void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
boolean moveToState) {
if (isPop) {
//出栈
record.executePopOps(moveToState);
} else {
//进栈
record.executeOps();
}
//省略
}
excuteOps
这个方法非常多的操作, 都是我们调用add, hide, show的时候添加进来的Op对象
这里看看hideFragmnet
public void hideFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "hide: " + fragment);
if (!fragment.mHidden) {
fragment.mHidden = true;
// Toggle hidden changed so that if a fragment goes through show/hide/show
// it doesn't go through the animation.
fragment.mHiddenChanged = !fragment.mHiddenChanged;
}
}
真相大白, 看到mHiddenChanged被修改了. 但是哪里通知fragment的呢?
在做完一系列的add,hide,show之后, 会调用moveToState, 然后会调用moveFragmentToExpectedState, 最后有一句:
if (f.mHiddenChanged) {
completeShowHideFragment(f);
}
最后会调用
fragment.mHiddenChanged = false;
fragment.onHiddenChanged(fragment.mHidden);
看完这里, 我们把onHiddenChanged的路径找到了.
总结
Fragment的add,show,hide一定要一起用才能保证生命周期.