简单使用
建议在看这边博文前,先看下这篇:Fragment之设计思路
先来看看Fragment的简单使用,然后再跟下去:
SimpleFragment fragment = new SimpleFragment();
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(parentViewId,fragment,"simple_fragment").commit();
这段代码很常见,但是这里面做了什么事情,可能就不知道了,那就来看看了。先来看下getSupportFragmentManager(),它实际返回的是FragmentManagerImpl对象,在FragmentHostCallback中实例化的,这在Fragment之设计思路中有讲到,接下来调用的是beginTransaction(),那就到FragmentManager中去看看它做了些什么操作:
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
很简单,就是创建了一个BackStackRecord对象,这里就简称为事物对象了,其内部就是记录即将添加的Fragment,使用的是双向链表的方式,这里先来看下记录Fragment双向链表的结构:
static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
static final int OP_DETACH = 6;
static final int OP_ATTACH = 7;
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
这个链表结构还是很好理解的,prev就是在这个之前添加的,next就是在这个之后添加的,Fragment就是当前添加的Fragment,cmd可以理解为一个标识,他标记的是当前Fragment是通过add()、remove(),replace()、hide()、show()、attch()、detach()这些方法中哪一个方法添加进来的,这些方法中有些是需要传view的id的,这些id所代表的view将会作为fragment生成view的父view,这里先大概说下这些方法的作用:
add()
会将fragment添加进FragmentManager进行管理,如果是带有viewid的那个方法,那么fragment生成的view默认是显示的;
remove()
会将之前添加进来的fragment从FragmentManager中移除;
replace()
这个需要传一个viewId,就是说需要将之前添加到这个viewId下fragment全部替换掉,内部实现替换可分为两步,一是先将这个viewId下的fragment移除,相当于调用了remove(),再将这个viewId下的所有fragment移除后,然后再将替换的fragment添加进来,相当于调用了add(),可以说这个方法是add()和remove()的组合;
hide()
调用这个方法的前提是已经有对应的fragment添加进来了,要知道每个fragment内部都可以维护一个view,这个view默认是显示的,当然要有对应的父view才能看到,当调用hide()时,其内部实际调用的 setVisibility(View.GONE)
show()
上面说了hide(),这里要理解show就简单多了, 其内部实际调用的就是setVisibility(View.GONE)
detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach()
重建view视图,附加到UI上并显示。
方法的大概意思就说到这,接下来就去源码中看看,毕竟最好的理解就是看源码,接着回到上面的流程,现在要说的就是这个add()方法:
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
void addOp(Op op) {
if (mHead == null) {
mHead = mTail = op;
} else {
op.prev = mTail;
mTail.next = op;
mTail = op;
}
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
mNumOp++;
}
下来看看add()中几个参数的意思:
1、containerViewId:这是一个view的id,后面fragment生成的view将会作为子view添加到它的下面;
2、fragment:这个好理解,就是需要添加的fragment;
3、tag:当前fragment的一个标识,fragment添加到FragmentManager中后,当需要从FragmentManager中再次拿到这个fragment时,就可以用到这个tag
在add()方法中,就是简单调用了doAddOp():
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
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;
}
if (containerViewId != 0) {
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;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
还是很好理解的,先是对Fragment中的几个成员变量(mFragmentManager ,mTag ,mContainerId,mFragmentId )进行了赋值,赋值完后就是创建了一个链表结构的对象,Op类的结构上面已经提到,最后在调用的addOp():
void addOp(Op op) {
if (mHead == null) {
mHead = mTail = op;
} else {
op.prev = mTail;
mTail.next = op;
mTail = op;
}
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
mNumOp++;
}
这里做的就是将创建的Op对象形成一个链表结构,以及赋值一些fragment中view显示的一些动画。add()方法的整个过程就分析完了,对add()理解了,其他方法也就好理解,基本都是对链表结构进行的操作,感兴趣的可以自己看下,很好理解的,代码如下:
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
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);
return this;
}
public FragmentTransaction remove(Fragment fragment) {
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction hide(Fragment fragment) {
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction show(Fragment fragment) {
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction detach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_DETACH;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction attach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_ATTACH;
op.fragment = fragment;
addOp(op);
return this;
}
说完add()后,接下来就是最后一步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(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
一个事物对象只能commit()一次,否则就会报 “commit already called” 异常,接着就是对mAddToBackStack的一个判断,这个值默认是false的,当调用了addToBackStack()后会赋值为true,它的作用就是当我们点击返回键时会返回到上一个fragment,而不会退出当前activity,这个可以看下FragmentActivity的onBackPressed():
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
supportFinishAfterTransition();
}
}
可以看到这里有个判断,如果当前Activity中有fragment添加到了回退栈中,那么就会显示添加到回退栈中的fragment,如果没有就会当前activity,好了,接下来要执行的就是FragmentManager的enqueueAction():
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
传进来两个参数,一是Runnable,BackStackRecord本身实现了这个接口,第二个allowStateLoss,是一个boolean值,这个值取决于提交事务时使用的方法:
public int commit() {
return commitInternal(false);
}
public int commitAllowingStateLoss() {
return commitInternal(true);
}
就是取决于这两方法,那调用这两个方法的区别在哪里?回到上面的enqueueAction(),可以看到allowStateLoss为false时会调用到一个方法,来看看里面做了什么:
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
就是抛了两个异常,这里的mStateSaved默认是为false的,那什么时候为true,比如在横竖屏切换时,activity是会销毁的,fragment也是会销毁然后重建,重建的时候,这时mStateSaved就会赋值为true,进入这里时就会抛异常了,到这,就可以顺带把commit()和commitAllowingStateLoss()区别总结下了:commit()方法提交的事物会检查fragment创建的状态,比如横竖屏切换后,检查状态时就会报异常,而commitAllowingStateLoss()提交的事物就不会去检查状态,这也就是这个两个方法最最本质的区别。
开了点小差,再次回到主线enqueueAction()方法中来,这里最终会去执行mExecCommit对象的run():
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
看来实现逻辑在execPendingActions()中:
public boolean execPendingActions() {
...
boolean didSomething = false;
while (true) {
int numActions;
synchronized (this) {
if (mPendingActions == null || mPendingActions.size() == 0) {
break;
}
//enqueueAction()中实例化的集合对象,不记得的可以再去看下enqueueAction
//下面的这些操作主要作用是mPendingActions中保存的对象复制到mTmpActions数组中去
numActions = mPendingActions.size();
if (mTmpActions == null || mTmpActions.length < numActions) {
mTmpActions = new Runnable[numActions];
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
}
mExecutingActions = true;
//拿到复制的数据后,接下来就是开始执行里面的run()方法了,前面有提到BackStackRecord实现了Runnable接口,下面的循环就是去执行实现的run方法
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}
//首次进来默认为false,如果进入到这里,那么就会将fragment的状态执行的与activity一致,这里就不跟下去了
if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null && f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
if (!loadersRunning) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
return didSomething;
}
对于这个方法的作用代码中有注释,这里就不多解释了,接下来就是去看看BackStackRecord中run()方法的实现了:
public void run() {
...
Op op = mHead;
while (op != null) {
int enterAnim = state != null ? 0 : op.enterAnim;
int exitAnim = state != null ? 0 : op.exitAnim;
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break;
case OP_REPLACE: {
Fragment f = op.fragment;
int containerId = f.mContainerId;
if (mManager.mAdded != null) {
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (FragmentManagerImpl.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
if (old.mContainerId == containerId) {
if (old == f) {
op.fragment = f = null;
} else {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old);
old.mNextAnim = exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ old + " to " + old.mBackStackNesting);
}
mManager.removeFragment(old, transition, transitionStyle);
}
}
}
}
if (f != null) {
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
}
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.removeFragment(f, transition, transitionStyle);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.hideFragment(f, transition, transitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.showFragment(f, transition, transitionStyle);
} break;
case OP_DETACH: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.detachFragment(f, transition, transitionStyle);
} break;
case OP_ATTACH: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.attachFragment(f, transition, transitionStyle);
} break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
op = op.next;
}
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
前面添加进来的fragment目前还是以链表的形式保存着,这里所作的操作就是对这个链表进行遍历,然后根据不同的状态做不同的操作,这里以add()为例:
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
很简单,就是调用了FragmentManager的addFragment():
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
整体的逻辑还是很简单,先是将fragment添加到mAdded集合,接着就是对fragment初始化一些属性,moveToStateNow传过来是false,所以下面那个方法也就不会执行了,这里在去看下makeActive(fragment):
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
}
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
目的就是将fragment添加到mActive集合中,后面同步fragment状态时,用到的就是这个集合,addFragment()就分析完了,总结下,就是将fragment添加到mAdd和mActive集合中以及初始化一些fragment中的成员变量。
到这还没结束,还有最重要的一点没说,继续回到BackStackRecord的run()方法中,在最后有一个方法:
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
这个方法就是同步fragment状态,里面会同步所有fragment的状态,来看下:
void moveToState(int newState, int transit, int transitStyle, boolean always) {
...
if (mActive != null) {
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null) {
moveToState(f, newState, transit, transitStyle, false);
...
}
}
...
}
}
简化了下代码,逻辑还是很清晰的,遍历所有添加进来的fragment,然后执行moveToState(f, newState, transit, transitStyle, false):
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
...
if (f.mState < newState) {
switch (f.mState) {
case Fragment.INITIALIZING:
...
f.onAttach(mHost.getContext());
...
if (f.mParentFragment == null) {
mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
}
f.mRetaining = false;
if (f.mFromLayout) {
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mView != null) {
...
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
} else {
f.mInnerView = null;
}
}
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
...
f.mContainer = container;
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
...
if (container != null) {
...
container.addView(f.mView);
}
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
case Fragment.ACTIVITY_CREATED:
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.mResumed = true;
f.performResume();
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
}
} else if (f.mState > newState) {
...
}
f.mState = newState;
}
这里简化了很多代码,当activity的生命周期改变时,都会调用到这里,这里就是去同步fragment的生命周期,fragment的生命周期方法在这里都可以找到调用,这里在来看点上面的伪代码:
f.mContainer = container;
f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
container.addView(f.mView);
这里container是activity给到fragment的父布局,在创建view后,就将这个view添加到父布局中,这个moveToState()方法可以好好品味下,对理解fragment很有帮助。好了,一下子发现扯了好多了,就到这里了。欢迎留言一起学习。