想想在fragment出现前我们如何实现类似fragment的功能呢?需求是要做一个相对独立的布局功能,可能在多个地方需要复用到,还有就是他需要能够被动态的切换,于是乎,写个layout,通过addView/removeView的使用,添加/删除 到另外一个父layout中,从而实现layout的复用及切换。但是子layout的背后的逻辑呢?感觉想起来就麻烦。
这个时候fragment的出现弥补了这个短板,他将一个相对独立的布局功能给完善的封装起来,这里不光有View,还关联了业务逻辑,另外它有自己的生命周期,便于其下资源的申请和释放。可以看的出 fragment虽小,但是五脏俱全,它是更小量级的Activity。这岂不更加符合我们对代码复用的追求.
在我们开始设计一个页面的时候,应该将该页面中功能相对独立的布局组件用fragment封装使用,这样不至于在产品要求去适配其他屏幕或者大幅度修改页面结构的时候要推倒重来。
工程当中fragment使用甚多,fragment中又嵌套使用多级fragment, 使用场合越来越复杂,这种情况下我们如何正确的使用且避免问题就需要从了解其根本机制。
1.Activity中有多个fragment, 如何实现其添加,删除 等等操作
2.在fragment中又嵌套使用多个fragment
1.将fragment添加到Activity中合适的位置
2.将fragment 和 Activity的生命周期相绑定
在Activity中需要这么一个fragment管理器 简称 fm吧,这些都是fm的事情。
来看看Activity的源码里有一段:
final FragmentManagerImpl mFragments = new FragmentManagerImpl();
在Activity生命周期运转之时,所调用时序图如下:
Activity->Activity:onCreate()
Activity->FragmentManagerImpl:dispatchActivityCreated()
FragmentManagerImpl->FragmentManagerImpl:moveToState(Fragment.ACTIVITY_CREATED, false);
结论:在Activity的生命周期关键函数中去调用FM的对应函数,FM再调用其下管理的fragment的相应函数从而实现了联系。
在我们去操作Fragment的时候:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
usingContentFragment = new ContentFragment();
fragmentTransaction.replace(R.id.content_area, usingContentFragment);
fragmentTransaction.commit();
查看beginTransaction()函数:
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
FragmentTransaction<|–BackStackRecord
Runnable<|–BackStackRecord
BackStackRecord*–FragmentManagerImpl
note left of BackStackRecord: 1.FragmentTransaction的实现类,实现add,replace等常见操作。\n 其并不实际的完成添加,而是将需要进行的fragment操作变成Op对象,并用链表保存记录。\n2.实现Runnable,在run方法中,按照链表提供的Op对象信息逐一进行add,replace等操作。
abstract class FragmentTransaction {
public abstract FragmentTransaction add(Fragment fragment, String tag);
public abstract FragmentTransaction add(int containerViewId, Fragment fragment);
public abstract FragmentTransaction add(int containerViewId, Fragment fragment, String tag);
public abstract FragmentTransaction replace(int containerViewId, Fragment fragment);
public abstract FragmentTransaction replace(int containerViewId, Fragment fragment, String tag);
public abstract FragmentTransaction remove(Fragment fragment);
public abstract FragmentTransaction hide(Fragment fragment);
public abstract FragmentTransaction show(Fragment fragment);
public abstract FragmentTransaction detach(Fragment fragment);
public abstract FragmentTransaction attach(Fragment fragment);
public abstract boolean isEmpty();
}
那么时序图呢?
BackStackRecord->BackStackRecord:commit()
BackStackRecord->BackStackRecord:commitInternal(boolean allowStateLoss)
BackStackRecord->FragmentManagerImpl:enqueueAction(this, allowStateLoss)
在enqueueAction函数中将BackStackRecord实例(其本身是Runnable的子类)添加到队列mPendingActions中
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mActivity == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mActivity.mHandler.removeCallbacks(mExecCommit);
mActivity.mHandler.post(mExecCommit);
}
}
}
那为什么又要执行mExecCommit
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
而在execPendingActions()方法中
for (int i=0; i<numActions; i++) { mTmpActions[i].run(); mTmpActions[i] = null; }
mTmpActions是mPendingActions的副本,逐一执行mTmpActions中的Runable.这又要回到BackStackRecord到底干了什么?
就列出一个小片段:
while (op != null) {
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
break;
...
op = op.next;
}
最上面提到BackStackRecord将所有的操作以Op的形式存放在一个链表中,那么当执行run函数的时候,就会依照链表来逐一执行Op;
mManager.addFragment(f, false); 最终使用的是FragmentManagerImpl.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);
}
}
}
在上面的FragmentManagerImpl.addFragment() 函数中,新增的fragment会添加到mAdded的list中.
从上面的过程来看,FragmentTransaction将对fragment的操作变成一个个的任务对象并管理起来。而真正去操作管理fragment的仍旧是FragmentManagerImpl
我们知道moveToState是实际推动fragment插入到父布局的函数。但是这里moveToStateNow=false. 那在什么地方去执行moveToState 呢?
我们再回到调用者BackStackRecord.run()中,在长长的switch结束后,会执行:
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
问题来了, Activity已处于前台工作状态时候添加fragment 和 在Activity初始之初添加的fragment,有什么区别,FM又是如何处理的呢?
removeFragment vs detachFragment vs hideFragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.enableDebugLogging(true);