最近刚好遇到了一个问题,新需求快稳省测试发现,应用Home键回到桌面后,电流没有回归,抓了一份systrace,发现ui进程还在跟SurfaceFlinger通信,有animation动画和binder通信,应该是一个VSync信号就有一次通信,花了一天时间才找出来,是同事在Fragment的onViewCreated方法里,通过listParent去加载布局,布局里有个动画的控件,而这个view是没有正常onPause的,所以后台一直在加载动画。
踩坑之后,决定研究一下Fragment的原理。
相关类
主要有四个类,FragmentManager是个抽象类,实现在FragmentManagerImpl;
FragmentTransaction也是个抽象类,实现类是BackStackRecord。
Fragment的添加
第一种方法是直接将fragment添加到布局中。
如果在NewFragment的onCreateView中打印堆栈,会看到
----world.one.com.newworld.fragment.NewFragment.onCreateView(NewFragment.java:19)
----android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
----android.support.v4.app.FragmentManagerImpl.ensureInflatedFragmentView(FragmentManager.java:1689)
----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1431)
----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1684)
----android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1930)
----android.support.v4.app.FragmentManagerImpl.onCreateView(FragmentManager.java:3745)
----android.support.v4.app.FragmentController.onCreateView(FragmentController.java:120)
----android.support.v4.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:405)
----android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:387)
第二种方法是动态替换,将布局中的某个view替换成fragment
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new NewFragment()).commit();
虽然只有一行代码,但是调用的过程是很复杂的。
----world.one.com.newworld.fragment.NewFragment.onCreateView(NewFragment.java:19)
----android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
----android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
----android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
----android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
----android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
----android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
----android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
----android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
----android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
----android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
----android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
----android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
先看下add方法添加一个Fragment到布局的过程。实际上是通过doAddOp方法实现的,需要注意的是注释1处传入的opcmd是OP_ADD,需要跟replace方法区分。
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);//1
return this;
}
add方法通过doAddOp将fragment封装成一个Op对象。
1.将fragmentManager指定为当前manager;
2.如果tag存在,将fragment的tag指定为传入的tag;
3.将fragment的ContainerId和FragmentId都指定为传入的containerViewId;
4.将fragment和opcmd封装成一个Op对象,执行addOp。
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
if (mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1) {
final Class fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
if ((fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) {
throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
+ " must be a public static class to be properly recreated from"
+ " instance state.");
}
}
fragment.mFragmentManager = mManager;//1
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;//2
}
if (containerViewId != 0) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no id");
}
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;//3
}
addOp(new Op(opcmd, fragment));//4
}
BackStackRecord内部维持了一个ArrayList列表mOps,用来保存fragment封装后的Op对象。
void addOp(Op op) {
mOps.add(op);
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
}
replace方法跟add类似,都是通过doAddOp实现的,只是参数不同。注释1处传入的opcmd是OP_REPLACE。
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
doAddOp(containerViewId, fragment, tag, OP_REPLACE);//1
return this;
}
其他操作还有remove,hide,show等,实际上都是通过addOp方法往ArrayList中插入一个Op对象,只是它们的opcmd参数不同。
public FragmentTransaction remove(Fragment fragment) {
addOp(new Op(OP_REMOVE, fragment));
return this;
}
public FragmentTransaction hide(Fragment fragment) {
addOp(new Op(OP_HIDE, fragment));
return this;
}
public FragmentTransaction show(Fragment fragment) {
addOp(new Op(OP_SHOW, fragment));
return this;
}
public FragmentTransaction detach(Fragment fragment) {
addOp(new Op(OP_DETACH, fragment));
return this;
}
public FragmentTransaction attach(Fragment fragment) {
addOp(new Op(OP_ATTACH, fragment));
return this;
}
不管是什么操作,add,replace或remove,最后一步的操作是commit
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) {
throw new IllegalStateException("commit already called");
}
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
PrintWriter pw = new FastPrintWriter(logw, false, 1024);
dump(" ", null, pw, null);
pw.flush();
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
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) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}
最终实现是在executeOps,这里遍历mOps数组,根据cmd对每个数组执行操作。
void executeOps() {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.fragment;
if (f != null) {
f.setNextTransition(mTransition, mTransitionStyle);
}
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);
}
}
if (!mReorderingAllowed) {
// Added fragments are added at the end to comply with prior behavior.
mManager.moveToState(mManager.mCurState, true);
}
}
onCreateView和onViewCreated的区别
首先,它们在时序上是有先后的,onCreateView先执行,然后是onViewCreated。
参考
https://blog.csdn.net/u011240877/article/details/78132990
https://xiazdong.github.io/2017/06/15/android-fragment/