今天小编就为大家带来一篇介绍Fragment的常见面试题和答案的文章。小编觉得挺实用的,为此分享给大家做个参考。一起跟随小编过来看看吧。

一丶Fragment 的使用

实现很简单,创建一个的布局,然后在Activity里点击时替换Fragment

mFragmentManager = getSupportFragmentManager(); mFragmentManager.beginTransaction() .replace(R.id.fl_content, fragment) .commitAllowingStateLoss();

代码很简单,核心就三步:

创建Fragment获取FragmentManager调用事务,添加、替换

先来看看FragmentManager
####二丶FragmentManager

public abstract class FragmentManager {...}

FragmentManager是一个抽象类,定义了一些和 Fragment 相关的操作和内部类/接口。

2.1.怎么定义?

FragmentManager中定义的方法如下:

//开启一系列对 Fragments 的操作 public abstract FragmentTransaction beginTransaction(); //FragmentTransaction.commit() 是异步执行的,如果你想立即执行,可以调用这个方法 public abstract boolean executePendingTransactions(); //根据 ID 找到从 XML 解析出来的或者事务中添加的 Fragment //首先会找添加到 FragmentManager 中的,找不到就去回退栈里找 public abstract Fragment findFragmentById(@IdRes int id); //跟上面的类似,不同的是使用 tag 进行查找 public abstract Fragment findFragmentByTag(String tag); //弹出回退栈中栈顶的 Fragment,异步执行的 public abstract void popBackStack(); //立即弹出回退栈中栈顶的,直接执行哦 public abstract boolean popBackStackImmediate(); ......

可以看到,定义的方法有很多是异步执行的,后面看看它究竟是如何实现的异步。

2.2.内部类/接口:

BackStackEntryFragment后退栈中的一个元素onBackStackChangedListener:后退栈变动监听器

FragmentLifecycleCallbacks:FragmentManager中的Fragment生命周期监听

//后退栈中的一个元素 public interface BackStackEntry { //栈中该元素的唯一标识 public int getId(); //获取 FragmentTransaction#addToBackStack(String) 设置的名称 public String getName(); @StringRes public int getBreadCrumbTitleRes(); @StringRes public int getBreadCrumbShortTitleRes(); public CharSequence getBreadCrumbTitle();public CharSequence getBreadCrumbShortTitle(); }

可以看到BackStackEntry的接口比较简单,关键信息就是 ID 和 Name。

//在 Fragment 回退栈中有变化时回调 public interface OnBackStackChangedListener { public void onBackStackChanged(); }//FragmentManager 中的 Fragment 生命周期监听 public abstract static class FragmentLifecycleCallbacks { public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {} public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {} public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {} public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {} public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {} public void onFragmentStarted(FragmentManager fm, Fragment f) {} public void onFragmentResumed(FragmentManager fm, Fragment f) {} public void onFragmentPaused(FragmentManager fm, Fragment f) {} public void onFragmentStopped(FragmentManager fm, Fragment f) {} public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {} public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {} public void onFragmentDestroyed(FragmentManager fm, Fragment f) {} public void onFragmentDetached(FragmentManager fm, Fragment f) {} } }

熟悉 Fragment 生命周期的同学一定觉得很面熟,这个接口就是为我们提供一个FragmentManager所 有Fragment生命周期变化的回调。

小结:
可以看到,FragmentManager是一个抽象类,它定义了对一个Activity/Fragment中 添加进来的Fragment列表、Fragment回退栈的操作、管理。

2.3.怎么实现类?FragmentManagerImpl

FragmentManager定义的任务是由FragmentManagerImpl实现的。

主要成员:

final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory { ArrayList<OpGenerator> mPendingActions; Runnable[] mTmpActions; boolean mExecutingActions; ArrayList<Fragment> mActive; ArrayList<Fragment> mAdded; ArrayList<Integer> mAvailIndices; ArrayList<BackStackRecord> mBackStack; ArrayList<Fragment> mCreatedMenus; // Must be accessed while locked. ArrayList<BackStackRecord> mBackStackIndices; ArrayList<Integer> mAvailBackStackIndices; ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks; //... }

可以看到,FragmentManagerImpl中定义了 添加的、活跃的。以及回退栈的列表,这和FragmentManager的要求一致

接着还有当前的状态,当前Fragment的起始mParent,以及FragmentManagermHostmContainer

FragmentContainer就是一个接口,定义了关于布局的两个方法:

public abstract class FragmentContainer { @Nullable public abstract View onFindViewById(@IdRes int id); public abstract boolean onHasView(); }

FragmentHostCallback就复杂一点了,它提供了Fragment需要的信息,也定义了Fragment宿主应该做的操作:

public abstract class FragmentHostCallback<E> extends FragmentContainer { private final Activity mActivity; final Context mContext; private final Handler mHandler; final int mWindowAnimations; final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl(); //... }

我们知道,一般来说Fragment的宿主就两种:

ActivityFragment

比如FragmentActivity的内部类HostCallbacks就实现了这个抽象类:

class HostCallbacks extends FragmentHostCallback<FragmentActivity> { public HostCallbacks() { super(FragmentActivity.this /*fragmentActivity*/); } //... @Override public LayoutInflater onGetLayoutInflater() { return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.t his); } @Override public FragmentActivity onGetHost() { return FragmentActivity.this; } ...... }

我们再看看他对FragmentManager定义的关键方法是如何实现的。

@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }

beginTransaction()返回一个新的BackStackRecord,我们后面介绍。前面提到了,popBackStack()是一个异步操作,它是如何实现异步的呢?

@Override public void popBackStack() { enqueueAction(new PopBackStackState(null, -1, 0), false); } public void enqueueAction(OpGenerator 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<>(); } mPendingActions.add(action); scheduleCommit(); } } private void scheduleCommit() { synchronized (this) { boolean postponeReady = mPostponedTransactions != null && !mPostponedTransactions.isEmpty(); boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1; if (postponeReady || pendingReady) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); } } }

可以看到,调用到最后,是调用宿主中的Handler来发送任务的,so easy 嘛。其他的异步执行也是类似,就不赘述了。

后退栈相关方法:

ArrayList<BackStackRecord> mBackStack; @Override public int getBackStackEntryCount() { return mBackStack != null ? mBackStack.size() : 0; } @Override public BackStackEntry getBackStackEntryAt(int index) { return mBackStack.get(index); }

可以看到,开始事务和后退栈,返回/操作的都是BackStackRecord,我们来了解了解它是何方神圣。

三丶事务怎么用?

BackStackRecord继承了FragmentTransaction

final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

先来看看FragmentTransaction

3.1.FragmentTransaction

FragmentTransaction定义了一系列对 Fragment 的操作方法:

//它会调用 add(int, Fragment, String),其中第一个参数传的是 0 public abstract FragmentTransaction add(Fragment fragment, String tag); //它会调用 add(int, Fragment, String),其中第三个参数是 null public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment); //添加一个 Fragment 给 Activity 的最终实现 //第一个参数表示 Fragment 要放置的布局 id //第二个参数表示要添加的 Fragment,【注意】一个 Fragment 只能添加一次 //第三个参数选填,可以给 Fragment 设置一个 tag,后续可以使用这个 tag 查询它 public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment, @Nullable String tag); //调用 replace(int, Fragment, String),第三个参数传的是 null public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment); //替换宿主中一个已经存在的 fragment //这一个方法等价于先调用 remove(), 再调用 add() public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment, @Nullable String tag); //移除一个已经存在的 fragment //如果之前添加到宿主上,那它的布局也会被移除 public abstract FragmentTransaction remove(Fragment fragment); //隐藏一个已存的 fragment //其实就是将添加到宿主上的布局隐藏 public abstract FragmentTransaction hide(Fragment fragment); //显示前面隐藏的 fragment,这只适用于之前添加到宿主上的 fragment public abstract FragmentTransaction show(Fragment fragment); //将指定的 fragment 将布局上解除 //当调用这个方法时,fragment 的布局已经销毁了 public abstract FragmentTransaction detach(Fragment fragment); //当前面解除一个 fragment 的布局绑定后,调用这个方法可以重新绑定 //这将导致该 fragment 的布局重建,然后添加、展示到界面上 public abstract FragmentTransaction attach(Fragment fragment);

fragment的操作基本就这几步,我们知道,要完成对fragment的操作,最后还需要提交一下:

mFragmentManager.beginTransaction() .replace(R.id.fl_child, getChildFragment()) // .commit() .commitAllowingStateLoss();2.2.事务的四种提交方式

事务最终的提交方法有四种:

commit()commitAllowingStateLoss()commitNow()commitNowAllowingStateLoss()

它们之间的特点及区别如下:

public abstract int commit();

commit()在主线程中异步执行,其实也是Handler抛出任务,等待主线程调度执行。

注意:
commit()需要在宿主Activity保存状态之前调用,否则会报错。
这是因为如果Activity出现异常需要恢复状态,在保存状态之后的commit()将会丢失,这和调用的初衷不符,所以会报错。

public abstract int commitAllowingStateLoss();

commitAllowingStateLoss()也是异步执行,但它的不同之处在于,允许在Activity保存状态之后调用,也就是说它遇到状态丢失不会报错。

因此我们一般在界面状态出错是可以接受的情况下使用它。

public abstract void commitNow();

commitNow()是同步执行的,立即提交任务。

前面提到FragmentManager.executePendingTransactions()也可以实现立即提交事务。但我们一般建议使用commitNow(), 因为另外那位是一下子执行所有待执行的任务,可能会把当前所有的事务都一下子执行了,这有可能有副作用。

此外,这个方法提交的事务可能不会被添加到FragmentManger的后退栈,因为你这样直接提交,有可能影响其他异步执行任务在栈中的顺序。

commit()一样,commitNow()也必须在Activity保存状态前调用,否则会抛异常。

public abstract void commitNowAllowingStateLoss();

同步执行的commitAllowingStateLoss()
OK,了解了FragmentTransaction定义的操作,去看看我们真正关心的、beginTransaction()中返回的BackStackRecord:

@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }3.3事务真正实现/回退栈BackStackRecord

BackStackRecord既是对Fragment进行操作的事务的真正实现,也是FragmentManager中的回退栈的实现:

final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

它的关键成员:

final FragmentManagerImpl mManager; //Op 可选的状态值 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; ArrayList<Op> mOps = new ArrayList<>(); static final class Op { int cmd; //状态 Fragment fragment; int enterAnim; int exitAnim; int popEnterAnim; int popExitAnim; } int mIndex = -1; //栈中最后一个元素的索引 }

可以看到 Op 就是添加了状态和动画信息的FragmentmOps就是栈中所有的Fragment。事务定义的方法它是如何实现的呢

先看添加一个Fragment到布局add()的实现:

@Override public FragmentTransaction add(int containerViewId, Fragment fragment) { doAddOp(containerViewId, fragment, null, OP_ADD); return this; ...... }

可以看到添加一个Fragment到布局很简单,概况一下就是:
修改fragmentManager和 ID,构造成 Op,设置状态信息,然后添加到列表里。

添加完了看看替换replace的实现:

@Override public FragmentTransaction replace(int containerViewId, Fragment fragment) { return replace(containerViewId, fragment, null); } @Override 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; }

太可怕了,也是调用上面刚提到的doAddOp(),不同之处在于第四个参数为OP_REPLACE,看来之前小看了这个状态值!

再看其他方法的实现就很简单了,无非就是构造一个 Op,设置对应的状态值。

@Override public FragmentTransaction remove(Fragment fragment) { Op op = new Op(); op.cmd = OP_REMOVE; op.fragment = fragment; addOp(op); return this; } @Override public FragmentTransaction hide(Fragment fragment) { Op op = new Op(); op.cmd = OP_HIDE; op.fragment = fragment; addOp(op); return this; } @Override public FragmentTransaction show(Fragment fragment) { Op op = new Op(); op.cmd = OP_SHOW; op.fragment = fragment; addOp(op); return this; }

那这些状态值的不同是什么时候起作用的呢?
别忘了我们操作Fragment还有最后一步,提交。
看看这两个是怎么实现的:

@Override public int commit() { return commitInternal(false); } @Override public int commitAllowingStateLoss() { return commitInternal(true); } int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); //... } }

前面已经介绍过了,FragmentManager.enqueueAction()最终是使用Handler实现的异步执行。
现在的问题是执行的任务是啥?
答案就是Handler发送的任务mExecCommit:
代码多了一点省略掉了,但我们终于找到了最终的实现:Handler异步发到主线,调度执行后,聚合、修改 Ops的状态,然后遍历、修改Fragment栈中的 View 的状态。

3.4.真正处理的部分

前面主要是对Fragment的包装类 Ops 进行一些状态修改,真正根据 Ops 状态进行操作在这个部分:

/** * 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++) { final Op op = mOps.get(opNum); final Fragment f = op.fragment; 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; default: throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } if (!mAllowOptimization && op.cmd != OP_ADD) { mManager.moveFragmentToExpectedState(f); } } if (!mAllowOptimization) { // Added fragments are added at the end to comply with prior behavior.mManager.moveToState(mManager.mCurState, true); } }

FragmentManager对这些方法的实现也很简单,修改Fragment的状态值,比如remove(Fragment) :

public void removeFragment(Fragment fragment) { if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); final boolean inactive = !fragment.isInBackStack(); if (!fragment.mDetached || inactive) { if (mAdded != null) { mAdded.remove(fragment); } if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; //设置属性值 fragment.mRemoving = true; } }

代码很长,先省略掉......但做的事情很简单:

根据状态调用对应的生命周期方法如果是新创建的,就把布局添加到ViewGroup

以上就是Fragment常见面试题和答案的详细内容了,看完之后是否有所收获呢?如果想了解更多相关内容,欢迎关注亿速云行业资讯!