Fragment的使用方式
class TestActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
val firstFragment = FirstFragment()
val secondFragment = SecondFragment()
val thirdFragment = ThirdFragment()
// 大部分的Fragment的使用方法都如下所示
// 所以就从Fragment的基础使用开始入手分析
supportFragmentManager
.beginTransaction()
.add(R.id.container, firstFragment)
.replace(R.id.container,secondFragment)
.remove(secondFragment)
.remove(firstFragment)
.add(R.id.container,thirdFragment)
.addToBackStack(null)
.commit()
}
}
FragmentManager的来源
跟踪AppCompatActivity的getSupportFragmentmanager()方法可以发现:
//------------------------------FragmentActivity.java------------------------------
public class FragmentActivity extends ComponentActivity implements
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
...
// 赋值mFragments创建一个FragmentController实例,并传递HostCallbacks实例
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
...
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
@NonNull
public FragmentManager getSupportFragmentManager() {
// 直接委托给FragmentController实例对象mFragments的getSupportFragmentManager方法
return mFragments.getSupportFragmentManager();
}
...
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
// 通过FragmentController调度Fragment的destroy生命周期
mFragments.dispatchDestroy();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}
...
/**
* Dispatch onPause() to fragments.
*/
@Override
protected void onPause() {
super.onPause();
mResumed = false;
// 通过FragmentController调度Fragment的pause生命周期
mFragments.dispatchPause();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
...
/**
* Dispatch onResume() to fragments.
*/
@Override
protected void onPostResume() {
super.onPostResume();
onResumeFragments();
}
/**
* This is the fragment-orientated version of {@link #onResume()} that you
* can override to perform operations in the Activity at the same point
* where its fragments are resumed. Be sure to always call through to
* the super-class.
*/
protected void onResumeFragments() {
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
// 通过FragmentController调度Fragment的resume生命周期
mFragments.dispatchResume();
}
...
/**
* Dispatch onStart() to all fragments.
*/
@Override
protected void onStart() {
super.onStart();
mStopped = false;
if (!mCreated) {
mCreated = true;
mFragments.dispatchActivityCreated();
}
mFragments.noteStateNotSaved();
mFragments.execPendingActions();
// NOTE: HC onStart goes here.
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
// 通过FragmentController调度Fragment的resume生命周期
mFragments.dispatchStart();
}
/**
* Dispatch onStop() to all fragments.
*/
@Override
protected void onStop() {
super.onStop();
mStopped = true;
markFragmentsCreated();
// 通过FragmentController调度Fragment的resume生命周期
mFragments.dispatchStop();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
...
// HostCallbacks的继承自FragmentHostCallback,后面会介绍该类的用途
class HostCallbacks extends FragmentHostCallback implements
ViewModelStoreOwner,
OnBackPressedDispatcherOwner {
...
}
}
- 由于AppCompatActivity继承自FragmentActivity,并且没有覆写getSupportFragmentManager,所以追踪到
FragmentActivity#getSupportFragmentManager中。 - 可以看到FragmentActivity将getSupportFragmentManager直接委托给了FragmentController对象。而且不仅仅是getSupportFragmentManager的委托,在FragmentActivity的各个生命周期的方法中也是将Fragment的其它生命周期交由FragmentController进行调度。
接着看看FragmentController这个类
FragmentController类
//------------------------------FragmentController.java------------------------------
public class FragmentController {
private final FragmentHostCallback> mHost;
/**
* Returns a {@link FragmentController}.
*/
@NonNull
public static FragmentController createController(@NonNull FragmentHostCallback> callbacks) {
return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
}
private FragmentController(FragmentHostCallback> callbacks) {
mHost = callbacks;
}
/**
* Returns a {@link FragmentManager} for this controller.
*/
// 将持有的FragmentHostCallback实例mHost的mFragmentManager返回
@NonNull
public FragmentManager getSupportFragmentManager() {
return mHost.mFragmentManager;
}
...
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the create state.
* Call when Fragments should be created.
*
* @see Fragment#onCreate(Bundle)
*/
public void dispatchCreate() {
mHost.mFragmentManager.dispatchCreate();
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the activity created state.
*
Call when Fragments should be informed their host has been created.
*
* @see Fragment#onActivityCreated(Bundle)
*/
public void dispatchActivityCreated() {
mHost.mFragmentManager.dispatchActivityCreated();
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the start state.
*
Call when Fragments should be started.
*
* @see Fragment#onStart()
*/
public void dispatchStart() {
mHost.mFragmentManager.dispatchStart();
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the resume state.
*
Call when Fragments should be resumed.
*
* @see Fragment#onResume()
*/
public void dispatchResume() {
mHost.mFragmentManager.dispatchResume();
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the pause state.
*
Call when Fragments should be paused.
*
* @see Fragment#onPause()
*/
public void dispatchPause() {
mHost.mFragmentManager.dispatchPause();
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the stop state.
*
Call when Fragments should be stopped.
*
* @see Fragment#onStop()
*/
public void dispatchStop() {
mHost.mFragmentManager.dispatchStop();
}
/**
* @deprecated This functionality has been rolled into {@link #dispatchStop()}.
*/
@Deprecated
public void dispatchReallyStop() {
}
/**
* Moves all Fragments managed by the controller's FragmentManager
* into the destroy view state.
*
Call when the Fragment's views should be destroyed.
*
* @see Fragment#onDestroyView()
*/
public void dispatchDestroyView() {
mHost.mFragmentManager.dispatchDestroyView();
}
/**
* Moves Fragments managed by the controller's FragmentManager
* into the destroy state.
*
* If the {@link androidx.fragment.app.FragmentHostCallback} is an instance of {@link ViewModelStoreOwner},
* then retained Fragments and any other non configuration state such as any
* {@link androidx.lifecycle.ViewModel} attached to Fragments will only be destroyed if
* {@link androidx.lifecycle.ViewModelStore#clear()} is called prior to this method.
*
* Otherwise, the FragmentManager will look to see if the
* {@link FragmentHostCallback host's} Context is an {@link Activity}
* and if {@link Activity#isChangingConfigurations()} returns true. In only that case
* will non configuration state be retained.
*
Call when Fragments should be destroyed.
*
* @see Fragment#onDestroy()
*/
public void dispatchDestroy() {
mHost.mFragmentManager.dispatchDestroy();
}
...
}
- 首先可以看到FragmentController的构造函数传递了FragmentHostCallback实例并赋值给mHost,而这个实例则是在FragmentActivity调用FragmentController#createController传递过来的。
- 最终getSupportFragmentManager返回的就是mHost的mFragmentManager。
- 生命周期的调度方法也是由mHost的mFragmentManager对象完成的。
那么mHost到底是一个怎样的存在呢?
HostCallbacks类
HostCallbacks类是FragmentActivity的内部类,继承自FragmentHostCallback(在上文FragmentActivity的源码截取中有体现)。那来看看FragmentHostCallback
//------------------------------FragmentHostCallback.java------------------------------
/**
* Integration points with the Fragment host.
*
* Fragments may be hosted by any object; such as an {@link Activity}. In order to
* host fragments, implement {@link FragmentHostCallback}, overriding the methods
* applicable to the host.
*/
// 官方注释:Fragment可以被如何对象持有,例如(Activity),为了成为Fragment的宿主,必须继承FragmentHostCallback。
public abstract class FragmentHostCallback extends FragmentContainer {
...
}
根据类头注释可知,FragmentHostCallback是官方为Fragments的宿主写的抽象类,而作为Fragment的宿主则必须继承该抽象类,并为其覆写其中方法。
而继承自FragmentHostCallback的FragmentActivity$HostCallbacks,就是FragmentActivity可以成为Fragment宿主的原因所在。
思考一个问题
直接在FragmentActivity中使用FragmentManager也可以达到Fragment宿主的目的,为什么还要这样设计?
在我们的认知中,FragmentActivity是专门用来管理Fragment的类,事实上FragmentActivity和Fragment没有进行任何关联甚至没有任何Fragment的导包,仅仅通过创建FragmentController实例并实现了FragmentHostCallback接口,就可以实现对Fragment的管理工作。这样的设计可以屏蔽宿主对Fragment的直接引用,从而拓展了Fragment的应用场景,而不仅仅局限于Activity中。如果未来需要出现一个新的宿主,就会显得十分方便。
FragmentTransaction类
通过supportManager.beginTransaction()
就可以获得FragmentTransaction对象。FragmentTransaction是可以操作事务的对象,所谓的事务,就是可以一次性添加多个操作。比如Fragment使用方法中的add,replace,remove,remove,add可以组成是一个事务。也就是说一个事务可以存在多个操作。
事务具有原子性,也就是说事务中的操作要么全部成功,要么全部不成功,不会出现部分成功的情况。
追踪beginTransaction:
//------------------------------FragmentManagerImpl.java------------------------------
@NonNull
@Override
public FragmentTransaction beginTransaction() {
// 返回一个新创建的BackStackRecord对象
return new BackStackRecord(this);
}
可以发现返回的是一个BackStackRecord对象。BackStackRecord对象继续追踪
//------------------------------BackStackRecord.java------------------------------
/**
* Entry of an operation on the fragment back stack.
*/
// BackStackRecord继承自FragmentTransaction,是Fragment回退栈的一个元素
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
static final String TAG = FragmentManagerImpl.TAG;
final FragmentManagerImpl mManager;
...
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
...
BackStackRecord可以理解为Fragment回退栈的一个元素。由于继承自FragmentTransaction,BackStackRecord也可以理解成一个事务,这样设计的目的是在操作回退栈时,可以逆向执行事务,从而实现Fragment的出栈操作。
FragmentTransaction的操作
接下来我们分析下事务中如何进行Fragment的操作的,以supportFragmentManager.beginTransaction().add(R.id.container, firstFragment)
的add操作为例
//------------------------------FragmentTransaction.java------------------------------
/**
* Calls {@link #add(int, Fragment, String)} with a null tag.
*/
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
// 直接调用doAddOp方法
// 参数OP_ADD是一个int常量,代表的是本次操作是一个添加Fragment的操作
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
继续跟进doAddOp方法
//------------------------------FragmentTransaction.java------------------------------
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
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.");
}
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 (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;
}
// 将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法
addOp(new Op(opcmd, fragment));
}
doAddOp方法进行了参数的校验,然后将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法
//------------------------------FragmentTransaction.java------------------------------
ArrayList mOps = new ArrayList<>();
...
void addOp(Op op) {
mOps.add(op);
op.mEnterAnim = mEnterAnim;
op.mExitAnim = mExitAnim;
op.mPopEnterAnim = mPopEnterAnim;
op.mPopExitAnim = mPopExitAnim;
}
可以看到addOp方法,仅仅是将op对象存储到集合mOps中,然后就结束了。为什么存储起来,因为一次事务有可能有多个操作,所以要存储起来,等到事务执行时再统一执行。
接下来追踪replace的操作:
//------------------------------FragmentTransaction.java------------------------------
@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
// 调用doAddOp方法
// 参数OP_REPLACE是一个int常量,代表的是本次操作是一个替换Fragment的操作
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
可以看到replace操作,也是经过doAddOp校验参数之后,会将opcmd和fragment组装成一个Op对象,同样加入mOps集合中。
那也来看下remove操作:
//------------------------------FragmentTransaction.java------------------------------
@NonNull
public FragmentTransaction remove(@NonNull Fragment fragment) {
addOp(new Op(OP_REMOVE, fragment));
return this;
}
remove的操作,不需要经过校验参数,直接组装成Op对象,加入mOps集合中。
FragmentTransaction的提交
我们就以commit()
为例,进行追踪
//------------------------------FragmentTransaction.java------------------------------
public abstract int commit();
commit在FragmentTransaction是个抽象方法,而它的实现在BackStackRecord中
//------------------------------BackStackRecord.java------------------------------
@Override
public int commit() {
// 直接调用commitInternal方法,传递参数为false
return commitInternal(false);
}
// 关注allowStateLoss参数,allowStateLoss的作用是当一条事务执行前会进行宿主状态的检查。
// 如果宿主已经处于stop状态,再提交一条事务则会发生异常
// 举一个例子:如果宿主因为内存不足被回收,而宿主已经执行了onSaveInstanceState,而此时又调用commit方法提交了事务,此时若allowStateLoss为false,就有可能触发异常了
int commitInternal(boolean allowStateLoss) {
// mCommitted是一个标志位,表示一个事务最多只能被提交一次,多次提交会抛出异常
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(" ", pw);
pw.close();
}
// 对标志位进行赋值
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
// 调用FragmentManager的enqueueAction方法,把本次操作和allowStateLoss参数传递过去
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
//------------------------------BackStackRecord.java------------------------------
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
// 对allowStateLoss进行校验,如果allowStateLoss为false,则进入分支
// allowStateLoss设计的目的是提醒开发者,宿主已经进行了onSaveInstance方法,当宿主因为内存不足等问题被回收,重新恢复时,新提交的事务是不能被恢复。以免在状态恢复时,让用户看到更奇怪的现象
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<>();
}
// 将action,也就是本次提交的事务添加到了一条等待队列中
mPendingActions.add(action);
// 然后执行scheduleCommit
scheduleCommit();
}
}
private void checkStateLoss() {
// 如果宿主已经执行了onSaveInstanceState或者已经处于stop状态,则抛出异常
if (isStateSaved()) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
}
@Override
public boolean isStateSaved() {
// See saveAllState() for the explanation of this. We do this for
// all platform versions, to keep our behavior more consistent between
// them.
// 返回的是宿主是否已经执行了onSaveInstanceState或者已经处于了stop的状态
return mStateSaved || mStopped;
}
那么如何安全的提交事务呢?
FragmentTransaction的4种提交方式
- commit():如果在宿主执行了
onSaveInstanceState
之后再执行该操作,会抛出异常。属于异步操作(不是子线程操作,而是会将操作发送的主线程的轮询队列中,当主线程轮询到了才进行事务的操作。) - commitAllowStateLoss():如果在宿主执行了
onSaveInstanceState
之后再执行该操作,不会去检查宿主状态,不会抛出异常。但该操作不会被Activity记录,恢复时也就没办法恢复这些提交操作,所以该操作适用不重要的事务。同属于异步事务。 - commitNow():会立刻执行当前提交的
Transaction
事务。属于同步事务。 - commitNowAllowStateLoss():具备以上两者的特性,既是同步执行,也不会检查宿主的状态,有可能该操作不会被正确恢复。
一般情况选择commitNowAllowStateLoss()
,这样不会抛出异常中断用户的操作。
Fragment页面重叠
先来讲下页面的状态保存和恢复
-
onSaveInstanceState
负责页面的状态保存。 -
onRestoreInstanceState
以及onCreate
方法会进行状态的恢复。
onSaveInstanceState
和onRestoreInstanceState
并不一定会成对出现。
页面状态保存(即onSaveInstanceState
)的触发时机:
- Home键回到桌面
- 从最近列表跳转别的应用
- 横竖屏切换
- 内存不足时销毁重建
Fragment页面重叠的主要原因是**FragmentActivity本身已经具有Fragment状态保存和状态恢复的功能。当Activity被回收并重建时,Fragment也会有相应的恢复操作,如果除了FragmentActivity自身自带的恢复操作之外,开发者在Activity的生命周期中也进行了Fragment的创建操作,则会造成重叠的效果 ** 。
解决方法:
- 在添加Fragment之前先判断FragmentManager中是否已经存在需要添加的Fragment(通过Tag)
Fragment的懒加载
在新的androidx的包下,官方推荐使用ViewPager2来替代ViewPager来实现懒加载。
ViewPager2内部是根据RecyclerView来实现的,而且可以实现按需预加载。官方内置了ViewPager2组件的绑定适配器FragmentStateAdapter
。
FragmentStateAdapter
的作用是当页面切换时,可见的Fragment进入resume状态,不可见的都是进入onStart这个生命周期的状态,所以相当于实现了Fragment的懒加载。
如果想继续使用ViewPager的话,官方也提供了新的适配器FragmentStatePagerAdapter
,在创建FragmentStatePagerAdapter
可以传递两种Behavior
@Retention(RetentionPolicy.SOURCE)
@IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
private @interface Behavior { }
/**
* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
* fragment changes.
*
* @deprecated This behavior relies on the deprecated
* {@link Fragment#setUserVisibleHint(boolean)} API. Use
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
* {@link FragmentTransaction#setMaxLifecycle}.
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
@Deprecated
// 当ViewPager执行页面切换时,会执行setUserVisibleHint方法,这是向前兼容的
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
/**
* Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
* state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
*
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
// 当ViewPager执行页面切换时,只有可见的被选中的那个Fragment才会进入onResume状态,其它不可见的都会进入onStart
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
实际上ViewPager2的FragmentStateAdapter
和ViewPager的FragmentStatePagerAdapter
是两个相似的功能,在页面切换的时候不会再去执行Fragment#setUserVisibleHint(boolean)
方法了。