Fragment状态改变与管理

Posted ITRenj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Fragment状态改变与管理相关的知识,希望对你有一定的参考价值。

Fragment(二)状态改变与管理

博客对应的Demo地址:GitHubGitee

通过这篇博客,我们能知道以下问题:

  • FragmentmState 变化过程
  • FragmentManagermCurState 变化过程

在《Fragment(一)从源码角度看add和replace过程》中,我们知道了Fragment使用addreplace加载到页面的基本过程,最后是根据状态判断,然后调用对应的方法,但是并没有详细说明状态的变化过程,这篇文章我们就主要来说说这个过程。在Fragment 的整个生命周期中,主要的状态有两个,非别用 Fragment 自己的 mState 字段FragmentManager 中的 mCurState 字段 表示。

说明和注意

因为不同的使用方式和不同的源码版本(android.support和androidX,或者非支持包中的Fragment)会有些差别,特对当前文章的使用方式和源码库版本进行声明。

  • 1. 源码版本: AndroidX库,具体 androidx.fragment:1.3.4 版本

  • 2. 使用方式:直接在对应FragmentActivity的布局文件中使用 <fragment> 标签的方式

      // xml 布局中使用 fragment 标签,在 FragmentActivity 中调用 setContentView() 方法设置对应的布局id
      <fragment
          android:id="@+id/fragment_replace_default"
          android:name="com.renj.fragment.replace.ReplaceFragment1"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent" />
    

取值常量

首先看看他们的取值有哪些,两个取值都是用的 Fragment 中的常量:

static final int INITIALIZING = -1;          // Not yet attached. (默认,还依附定到容器)
static final int ATTACHED = 0;               // Attached to the host. (依附到容器)
static final int CREATED = 1;                // Created. (创建 Fragment)
static final int VIEW_CREATED = 2;           // View Created.(Fragment 的视图创建)
static final int AWAITING_EXIT_EFFECTS = 3;  // Downward state, awaiting exit effects (等待退出)
static final int ACTIVITY_CREATED = 4;       // Fully created, not started.(完全创建,未启动)
static final int STARTED = 5;                // Created and started, not resumed.(启动)
static final int AWAITING_ENTER_EFFECTS = 6; // Upward state, awaiting enter effects(等待进入)
static final int RESUMED = 7;                // Created started and resumed.(创建开始并恢复/可操作)

Fragment状态值和生命周期对应关系图

我们发现状态与我们的政策生命周期有些对不上,比如 Pause、Destory 等状态都没有对应的值,我们先通过一张图来看看它们是怎样表示 Fragment 整个生命周期的:

具体分析

我们一般使用的都是支持包(android.support或者 androidX,这篇博客主要以AndroidX为目标)中的Fragment,所以 Activity 就应该是 FragmentActivity 或者其子类,我们主要通过FragmentActivity的生命周期变化来看对应的Fragment的状态变化。如果想要了解 Activity 的生命周期过程,请移步 《简书:Activity 的组成》《CSDN:Android自定义View之Activity页面的组成》 ,查看相关内容。

FragmentActivity 构造

首先我们从 FragmentActivity 的构造方法开始看

// 定义 mFragments
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

public FragmentActivity() 
    super();
    init();


@ContentView
public FragmentActivity(@LayoutRes int contentLayoutId) 
    super(contentLayoutId);
    init();


private void init() 
	// 增加Context与FragmentActivity关联时的回调监听
    addOnContextAvailableListener(new OnContextAvailableListener() 
        @Override
        public void onContextAvailable(@NonNull Context context) 
			// 调用 FragmentController 的 attachHost() 方法
            mFragments.attachHost(null /*parent*/);
            Bundle savedInstanceState = getSavedStateRegistry()
                    .consumeRestoredStateForKey(FRAGMENTS_TAG);

            if (savedInstanceState != null) 
				// 用于恢复状态
                Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
                mFragments.restoreSaveState(p);
            
        
    );

查看 FragmentController#attachHost() 实现:

public void attachHost(@Nullable Fragment parent) 
    mHost.mFragmentManager.attachController(
            mHost, mHost /*container*/, parent);

内部没有做什么实质性的操作, 它就是一个中间层,调用 FragmentManager#attachController() 方法:

void attachController(@NonNull FragmentHostCallback<?> host,
        @NonNull FragmentContainer container, @Nullable final Fragment parent) 
    if (mHost != null) throw new IllegalStateException("Already attached");
    mHost = host;
    mContainer = container;
    mParent = parent;

	// ... 注册各种监听,省略

在方法中,除了赋值以外,就是注册各种监听,并无其他实际操作, 这里的 mHost 就是 FragmentActivity 内部类 HostCallbacks, mContainer 就是没有做什么实质工作的 FragmentContainer,这里直接也是使用的 mHost(因为 HostCallbacks 间接继承了 FragmentContainer), mParent 指的是要依附的 Fragment, 当前的过程是 Activity 驱动的, 所以不存在 Fragment, 该值为 null

目前步骤(FragmentActivity 构造)简单总结

  1. 启动一个 FragmentActivity
  2. FragmentActivity中创建类型为 FragmentController 的成员变量 mFragments,创建这个对象的需要一个 HostCallbacks 对象,所以也会创建,在 HostCallbacks 的父类 FragmentHostCallback 中有一个类型为 FragmentManager 的成员变量 mFragmentManager 也会创建(具体为 FragmentManager 的子类 FragmentManagerImpl ),
  3. 调用构造和内部的 init() 方法,进而调用FragmentController#attachHost()方法,最终调用FragmentManager#attachController() 方法,将相关值传递到 FragmentManager

接着看 FragmentActivity 中与 Fragment 相关的下一步回调

FragmentActivity#onCreate() 方法

onCreate() 方法表示 Activity 创建,在这个方法中我们会调用 setContentView() 方法设置页面布局,在 setContentView() 这个方法中会,会调用一个与Fragment相关的方法,我们看一下FragmentActivity#onCreate() 方法:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    mFragments.dispatchCreate();

先调用 super.onCreate() 方法,然后调用 mFragments.dispatchCreate()(也就是FragmentController#dispatchCreate()),而在我们自己的 Activity 中会调用 setContentView() 方法,在这个方法的调用过程中会回调到 FragmentActivityonCreateView() 方法(这个调用过程在文章底部 “扩展:ActivityonCreateView() 方法的调用过程” 可以查看),在这个方法中又会调用到 Fragment 中的相关方法。我们看看 FragmentActivity#onCreateView() 方法:

@Override
@Nullable
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
        @NonNull AttributeSet attrs) 
    final View v = dispatchFragmentsOnCreateView(parent, name, context, attrs);
    if (v == null) 
        return super.onCreateView(parent, name, context, attrs);
    
    return v;


@Nullable
final View dispatchFragmentsOnCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context, @NonNull AttributeSet attrs) 
    return mFragments.onCreateView(parent, name, context, attrs);

接着调用mFragmentsonCreateView() 方法,实际就是 FragmentController#onCreateView():

// FragmentController#onCreateView()
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
        @NonNull AttributeSet attrs) 
    return mHost.mFragmentManager.getLayoutInflaterFactory()
            .onCreateView(parent, name, context, attrs);

看看 FragmentManager#getLayoutInflaterFactory() 返回的是什么:

@NonNull
LayoutInflater.Factory2 getLayoutInflaterFactory() 
    return mLayoutInflaterFactory;


// FragmentManager 的成员变量
private final FragmentLayoutInflaterFactory mLayoutInflaterFactory =
        new FragmentLayoutInflaterFactory(this);

这里返回的 FragmentManager 中定义的一个成员变量 mLayoutInflaterFactory,具体的类型是 FragmentLayoutInflaterFactory,所以我们接着看 FragmentLayoutInflaterFactory#onCreateView() 方法:

@Override
public View onCreateView(@Nullable final View parent, @NonNull String name,
        @NonNull Context context, @NonNull AttributeSet attrs) 

    // ...

	// 设置 Fragment 成员变量 mFromLayout 和 mInLayout 的值为 true
	fragment.mFromLayout = true;
	fragment.mInLayout = true;
	// 1. 将 Fragment 添加到 mAdded 中
	fragmentStateManager = mFragmentManager.addFragment(fragment);

	// ...

    fragment.mContainer = (ViewGroup) parent;
	// 2. 修改Fragment状态并调用相关方法
    fragmentStateManager.moveToExpectedState();
	// 3. 继续调用相关回调方法
    fragmentStateManager.ensureInflatedView();

    // ...
    return fragment.mView;

1. FragmentManager.addFragment(fragment)

源码追踪:

// FragmentManager.addFragment(fragment)
FragmentStateManager addFragment(@NonNull Fragment fragment) 
    FragmentStateManager fragmentStateManager = createOrGetFragmentStateManager(fragment);
	// 调用 FragmentStore#addFragment(fragment)
    mFragmentStore.addFragment(fragment);
    return fragmentStateManager;


// FragmentStore#addFragment(fragment)
void addFragment(@NonNull Fragment fragment) 
    synchronized (mAdded) 
        mAdded.add(fragment);
    
    fragment.mAdded = true;

将当前的 Fragment 加入到 FragmentStore#mAdded 中,如果熟悉了《简书:Fragment(一)从源码角度看add和replace过程》《CSDN:Fragment(一)从源码角度看add和replace过程》这篇博客,对这个列表就不会感到陌生了,这里就不多说了。

2. FragmentStateManager.moveToExpectedState()

源码追踪:

void moveToExpectedState() 
	// 调用 computeExpectedState() 计算应该处于的状态值
    while ((newState = computeExpectedState()) != mFragment.mState) 
		// Fragment 打开
        if (newState > mFragment.mState) 
            // Moving upward
            int nextStep = mFragment.mState + 1;
            switch (nextStep) 
                case Fragment.ATTACHED:
                    attach();
                    break;
                case Fragment.CREATED:
                    create();
                    break;
                case Fragment.VIEW_CREATED:
                    ensureInflatedView();
                    createView();
                    break;
                case Fragment.AWAITING_EXIT_EFFECTS:
                    activityCreated();
                    break;
                case Fragment.ACTIVITY_CREATED:
                    if (mFragment.mView != null && mFragment.mContainer != null) 
                        SpecialEffectsController controller = SpecialEffectsController
                                .getOrCreateController(mFragment.mContainer,
                                        mFragment.getParentFragmentManager());
                        int visibility = mFragment.mView.getVisibility();
                        SpecialEffectsController.Operation.State finalState =
                                SpecialEffectsController.Operation.State.from(visibility);
                        controller.enqueueAdd(finalState, this);
                    
                    mFragment.mState = Fragment.ACTIVITY_CREATED;
                    break;
                case Fragment.STARTED:
                    start();
                    break;
                case Fragment.AWAITING_ENTER_EFFECTS:
                    mFragment.mState = Fragment.AWAITING_ENTER_EFFECTS;
                    break;
                case Fragment.RESUMED:
                    resume();
                    break;
            
         else 
			// Fragment 关闭
        
    

调用 computeExpectedState() 方法计算 Fragment 当前应该的状态值,如果当前的状态值与应该的状态值不一样,那么就进入循环,获取下一步的状态,然后调用相关方法,直到当前状态处于应该的状态。

注意:这里我将 else 部分的代码去掉了,因为在前面的分析是不会走到里面去的。在 Fragment 创建到显示的过程当中 newState > mFragment.mState 条件是一定成立的。else 的代码主要在 Fragment 退出的时候。在后面会放出来。

默认 Fragment#mState 的状态是 Fragment.INITIALIZING 的,但是走到这一步的时候,computeExpectedState() 方法计算出应该的状态值是 Fragment.VIEW_CREATED 的(具体的计算过程就不看贴出来,想详细了解的话可以通过debug的方式一步一步查看)。两个值不一样,所以需要进入循环:

  • 第一次进入循环:Fragment#mState 的值为 Fragment.INITIALIZING -> 计算出的应该值为 Fragment.VIEW_CREATED -> if 条件成立 -> nextStep 的值为 Fragment.mState+1,也就是 Fragment.ATTACHED -> 进而调用 FragmentStateManager#attach() 方法 -> Fragment#performAttach()在这个方法中会将 Fragment#mState 的值修改为 Fragment.ATTACHED 并且调用 Fragment#onAttach() 方法
  • 当前的值变为 Fragment.ATTACHED 后,继续循环计算和判断,第二次计算出的值还是 Fragment.VIEW_CREATED,与当前的不一样,进而进入 if -> nextStep 的值为 Fragment.mState+1,也就是 Fragment.CREATED -> 进而调用 FragmentStateManager#create() 方法 -> Fragment#performCreate()在这个方法中会将 Fragment#mState 的值修改为 Fragment.CREATED 并且调用 Fragment#onCreate() 方法
  • 当前的值变为 Fragment.CREATED 后,继续循环计算和判断,第三次计算出的值还是 Fragment.VIEW_CREATED,与当前的不一样,进而进入 if -> nextStep 的值为 Fragment.mState+1,也就是 Fragment.VIEW_CREATED -> 进而调用 FragmentStateManager#ensureInflatedView() 方法 和 FragmentStateManager#createView() 方法
    • FragmentStateManager#ensureInflatedView()在这个方法中会将 Fragment#mState 的值修改为 Fragment.VIEW_CREATED 】 -> Fragment#performViewCreated()在这个方法中会将 mPerformedCreateView 变量设置成true,并且调用 Fragment#onViewCreated() 方法
    • FragmentStateManager#createView() 这个方法的主要作用也是创建 FragmentView ,并且将 Fragment 的状态修改为 Fragment.VIEW_CREATED 并且回调对应的方法,但是在这里会直接返回,因为在前一步已经把 FragmentView 创建完成了,并且状态值也修改成对应的了

当循环到这里时,当前状态已经和计算出的应该状态一样了,循环结束,FragmentStateManager.moveToExpectedState() 流程结束,接着往下看。

3. FragmentStateManager#ensureInflatedView()

源码追踪:

// FragmentStateManager#ensureInflatedView()
void ensureInflatedView() 
    if (mFragment.mFromLayout && mFragment.mInLayout && !mFragment.mPerformedCreateView) 
        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) 
            Log.d(TAG, "moveto CREATE_VIEW: " + mFragment);
        
        mFragment.performCreateView(mFragment.performGetLayoutInflater(
                mFragment.mSavedFragmentState), null, mFragment.mSavedFragmentState);
        if (mFragment.mView != null) 
            mFragment.mView.setSaveFromParentEnabled(false);
            mFragment.mView.setTag(R.id.fragment_container_view_tag, mFragment);
            if (mFragment.mHidden) mFragment.mView.setVisibility(View.GONE);
            mFragment.performViewCreated();
            mDispatcher.dispatchOnFragmentViewCreated(
                    mFragment, mFragment.mView, mFragment.mSavedFragmentState, false);
            mFragment.mState = Fragment.VIEW_CREATED;
        
    

这一步同样是为了保证将 Fragment 的状态到相应值,并且回调对应方法。这里在进行 if 判断时不满足,因为第三个判断 mFragment.mPerformedCreateView 的值在上面的 Fragment#performViewCreated() 方法中已经设置为 true 了,所以 if 条件不满足,直接跳过。到此为止,Activity#setContentView() 影响 Fragment 的部分基本完成。

关于 FragmentActivity#onCreate() 的疑问

  • 为什么 FragmentActivity#onCreate() 中的 mFragments.dispatchCreate();方法 先调用,但是没有执行 Fragment 的相关方法?

    使用 fragment 标签时,虽然 FragmentActivity#onCreate() 方法中的 mFragments.dispatchCreate(); 会比 setContentView() 先调用,但是这个时候 Fragment 还没有加载到 FragmentStore#mAdded 中,所以它本身不会回调 Fragment 相关的方法。我们通过上面的分析知道了 Fragment 的增加是在 FragmentLayoutInflaterFactory#onCreateView() 方法中,也就是 fragment 创建完成时回调 FragmentActivity#onCreateView() 方法的过程中增加的。看一下 mFragments.dispatchCreate() 的调用过程:

    FragmentController#dispatchCreate() -> FragmentManager#dispatchCreate() -> FragmentManager#dispatchStateChange(Fragment.CREATED) -> FragmentManager#moveToState() -> FragmentStore#moveToExpectedState() -> FragmentStateManager#moveToExpectedState()

      // FragmentManager#moveToState()
      void moveToState(int newState, boolean always) 
      	// 修改FragmentManager 的 mCurState 字段值为新状态,这里也就是 Fragment.CREATED
          mCurState = newState;
    
      	// 调用 FragmentStore#moveToExpectedState()
          mFragmentStore.moveToExpectedState();
      
    
      // FragmentStore#moveToExpectedState()
      void moveToExpectedState() 
          for (Fragment f : mAdded) 
              FragmentStateManager fragmentStateManager = mActive.get(f.mWho);
              if (fragmentStateManager != null) 
                  fragmentStateManager.moveToExpectedState();
              
          
      	// ...
      
    

    在这个方法中遍历了 mAdded 列表,然后调用 FragmentStateManager#moveToExpectedState() 方法进而回调 Fragment 的相关方法,但是现在 mAdded 还是空的,所以不会调用相关方法。

到这里,Activity 方法的 onCreate() 方法执行完成了。

我们对这一步(FragmentActivity#onCreate() 方法)进行一个简单总结

  1. onCreate() 中调用 mFragments.dispatchCreate() ,但是这个方法不会回调到 Fragmnet 的相关方法,因为现在 fragmnet 标签还没解析到,不过会将 FragmentManagermCurState 字段值设置为 Fragment.CREATED
  2. 在调用 setContentView() 的过程中会通过一系列的调用,最终回调 FragmentFragment#onAttach()Fragment#onCreate()Fragment#onViewCreated() ,并且 Fragment 的状态值 mState 会变为 Fragment.VIEW_CREATED,具体的变化: Fragment.INITIALIZING -> Fragment.ATTACHED -> Fragment.CREATED -> Fragment.VIEW_CREATED

FragmentActivity#onStart() 方法

ActivityonCreate() 方法执行完之后,系统回调的下一个生命周期方法是 onStart() ,我们看一下 FragmentActivityonStart() 方法:

@Override
protected void onStart() 
    super.onStart();
    mStopped = false;
    if (!mCreated) 
        mCreated = true;
		// 1. 调用 dispatchActivityCreated() 方法
        mFragments.dispatchActivityCreated();
    

	// 2. 调用 dispatchStart() 方法
    mFragments.dispatchStart();

主要是通过 mFragments(FragmentController) 调用了dispatchActivityCreated()dispatchStart() 两个方法。

1. FragmentController#dispatchActivityCreated() 方法

源码追踪:

// FragmentController#dispatchActivityCreated()
public void dispatchActivityCreated() 
    mHost.mFragmentManager.dispatchActivityCreated();


// FragmentManager#dispatchActivityCreated()
void dispatchActivityCreated() 
    mStateSaved = false;
    mStopped = false;
    mNonConfig.setIsStateSaved(false);
    dispatchStateChange(Fragment.ACTIVITY_CREATED);


// FragmentManager#dispatchStateChange()
private void dispatchStateChange(int nextState) 
	// ...
	// 设置/修改 FragmentStateManager#mFragmentManagerState 的值
	 mFragmentStore.dispatchStateChange(nextState);
	// ...
	// 调用 FragmentManager#moveToState() 方法,这个方法
    moveToState(nextState, false);


// FragmentManager#moveToState()
void moveToState(int newState, boolean always) 
	// 修改FragmentManager 的 mCurState 字段值为新状态,这里也就是 Fragment.CREATED
    mCurState = newState;

	// 调用 FragmentStore#moveToExpectedState()
    mFragmentStore.moveToExpectedState();

通过源码我们发现调用到了 FragmentManager#moveToState() 方法,在这个方法中对 FragmentManagermCurState 字段值进行了修改,这里就是设置值为 Fragment.ACTIVITY_CREATED 了;接着调用 FragmentStore#moveToExpectedState() 方法,这个方法的内部主要就是循环计算和判断当前状态值与计算出来的应该值是否一样,进而调用对应的方法。这一步计算出来的应该的值是 Fragment.ACTIVITY_CREATED,所以 FragmentmState 值将会变为 Fragment.AWAITING_EXIT_EFFECTS 然后变为 Fragment.ACTIVITY_CREATED,在这个过程中会调用 FragmentStateManager#activityCreated(),进而调用 Fragment#performActivityCreated() -> Fragment#onActivityCreated() 回调。

2. FragmentController#dispatchStart() 方法

这个方法的调用过程和FragmentController#dispatchActivityCreated() 方法类似,就不再将源码都贴出来了,它主要通过 dispatchStateChange(Fragment.STARTED); 方法将FragmentManagermCurState 字段值设置为 Fragment.STARTED,然后通过 FragmentStore.moveToExpectedState() 方法进行循环判断,这一步将 Fragment 的状态设置成了 Fragment.STARTED,在这个过程中会调用 FragmentStateManager#start(),进而调用 Fragment#performStart() -> Fragment#onStart() 回调。

我们对这一步(FragmentActivity#onStart() 方法)进行一个简单总结

  1. onStart() 中调用 dispatchActivityCreated()dispatchStart() 两个方法;
  2. dispatchActivityCreated() 方法将 FragmentManager#mCurState 的值由上一步的 Fragment.CREATED 变为 Fragment.ACTIVITY_CREATED;并且将 Fragment#mState 的值改变,具体变化过程为: Fragment.VIEW_CREATED -> Fragment.AWAITING_EXIT_EFFECTS -> Fragment.ACTIVITY_CREATED;
  3. dispatchStart() 方法将 FragmentManager#mCurState 的值由上一步的 Fragment.ACTIVITY_CREATED 变为 Fragment.STARTED;并且将 Fragment#mState 的值改变,具体变化过程为: Fragment.ACTIVITY_CREATED -> Fragment.STARTED;

FragmentActivity#onResume() 方法

ActivityonCreate() 方法执行完之后,系统回调的下一个生命周期方法是 onResume() ,我们看一下 FragmentActivityonResume() 方法:

protected void onResume() 
    super.onResume();
    mResumed = true;
    mFragments.noteStateNotSaved();
    mFragments.execPendingActions();

我们发现并没有调用类似 dispatchXxx() 的方法,那么我们接着往上一层代码看,Activity#onResume() 方法的调用是在 Activity#performResume() 方法进而调用 Instrumentation#callActivityOnResume(this) 方法,在这个方法中 Activity#onResume() 方法被回调。那么我们先看一下 Activity#performResume()

final void performResume(boolean followedByPause, String reason) 
    // 回调 Activity 的 onResume() 方法
    mInstrumentation.callActivityOnResume(this);

	// 看上去就是调用了我们熟悉的方法,然后回调Fragment的方法
    mFragments.dispatchResume();

	// 调用 onPostResume() 方法
    onPostResume();

调用 mFragments.dispatchResume(); 了这个方法,这个方法看上去就是我们需要的,但是我们会发现这个因为是在 Activity 中调用,而不是 FragmentActivity 中调用,那么它使用的就是 android.app 包,所以不是我们需要的。

进而看一下 Activity#onPostResume() 方法:

// Activity#onPostResume()
protected void onPostResume() 
    final Window win = getWindow();
    if (win != null) win.makeActive();
    if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
    mCalled = true;

好像没有找到我们需要的,但是这是 Activity 中实现,我们发现 FragmentActivity 中重写了这个方法:

// FragmentActivity#onPostResume()
protected void onPostResume() 
    super.onPostResume();
    onResumeFragments();


// FragmentActivity#onResumeFragments()
protected void onResumeFragments() 
    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
    mFragments.dispatchResume();

通过查看源码发现最终在 FragmentActivity#onResumeFragments() 方法中找到了我们需要的方法 mFragments.dispatchResume()。前面已经对类似的方法说过好几次了,所以就不在一步一步看源码了,直接做一个总结。

我们对这一步(FragmentActivity#onResume() 方法)进行一个简单总结

  1. FragmentActivity#onResume() 中并没有调用mFragments.dispatchResume()方法,而是在 FragmentActivity#onResumeFragments() 中调用的。
  2. dispatchResume() 方法执行过程中会将 FragmentManager#mCurState 的值由上一步的 Fragment.STARTED 变为 Fragment.RESUMED;并且将 Fragment#mState 的值改变,具体变化过程为: Fragment.STARTED -> Fragment.AWAITING_ENTER_EFFECTS -> Fragment.RESUMED

到这一步执行完成,我们的 Fragment 就已经完全显示,并且用户可操作了。

关闭 FragmentmState 的变化过程

在文章开头时,我们将它的取值贴出来了,发现到了 RESUME 就没有了,那么我先关闭时 Activity 调用 onPause()onStop()onDestory() 时,Fragment#mState 的变化过程又是什么样,它们又用哪些值来表示呢?我们接着往下看。

FragmentActivity#onPause() 方法

源码追踪:

// FragmentActivity#onPause()
protected void onPause() 
    super.onPause();
    mResumed = false;
    mFragments.dispatchPause();
    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);


// FragmentController#dispatchPause()
public void dispatchPause() 
    mHost.mFragmentManager.dispatchPause();


// FragmentManager#dispatchPause()
void dispatchPause() 
    dispatchStateChange(Fragment.STARTED);

通过方法追踪我们发现调用 FragmentActivity#onPause() 方法后状态值又是传递的 Fragment.STARTED,通过调用最终方法肯定会走到 FragmentStateManager#moveToExpectedState() 方法,通过上面的分析我们应该都很熟悉了,所以中间的调用过程就不贴出来了。

void moveToExpectedState() 
    mMovingToState = true;
    int newState;
    while ((newState = computeExpectedState()) != mFragment.mState) 
        if (newState > mFragment.mState) 
        // Fragment 进入
         else 
            // Fragment 退出
            int nextStep = mFragment.mState - 1;
            switch (nextStep) 
                case Fragment.AWAITING_ENTER_EFFECTS:
                    pause();
                    break;
                case Fragment.STARTED:
                    mFragment.mState = Fragment.STARTED;
                    break;
                case Fragment.ACTIVITY_CREATED:
                    stop();
                    break;
                case Fragment.AWAITING_EXIT_EFFECTS:
                    if (FragmentManager.isLoggingEnabled(Log.DEBUG)) 
                        Log.d(TAG, "movefrom ACTIVITY_CREATED: " + mFragment);
                    
                    if (mFragment.mView != null) 
                        // Need to save the current view state if not done already
                        // by saveInstanceState()
                        if (mFragment.mSavedViewState == null) 
                            saveViewState();
                        
                    
                    if (mFragment.mView != null && mFragment.mContainer != null) 
                        SpecialEffectsController controller = SpecialEffectsController
                                .getOrCreateController(mFragment.mContainer,
                                        mFragment.getParentFragmentManager());
                        controller.enqueueRemove(this);
                    
                    mFragment.mState = Fragment.AWAITING_EXIT_EFFECTS;
                    break;
                case Fragment.VIEW_CREATED:
                    mFragment.mInLayout = false;
                    mFragment.mState = Fragment.VIEW_CREATED;
                    break;
                case Fragment.CREATED:
                    destroyFragmentView();
                    mFragment.mState = Fragment.CREATED;
                    break;
                case Fragment.ATTACHED:
                    destroy();
                    break;
                case Fragment.INITIALIZING:
                    detach();
                    break;
            
        
    

之前的过程已经将 if 当中的流程说了,所以这里就将哪些代码去掉了;通过之前的分析我们已经知道了 Fragment#mState 的值是 RESUMED 了,已经是取值范围中最大的了,所以 if 条件 if (newState > mFragment.mState) 肯定是不成立的了。现在只剩下 else 的代码,因为在 Fragment 退出的过程当中,走的都是 else 当中的代码。

computeExpectedState() 方法计算出的应该值是 Fragment.STARTED,和当前的 Fragment.RESUME 不一样,所以进入循环,这一步会经过两次循环:

case Fragment.AWAITING_ENTER_EFFECTS:
    pause();
    break;
case Fragment.STARTED:
    mFragment.mState = Fragment.STARTED;
    break;

// FragmentStateManager#pause()
void pause() 
    mFragment.performPause();
    mDispatcher.dispatchOnFragmentPaused(mFragment, false);


// Fragment#performPause()
void performPause() 
    mState = AWAITING_ENTER_EFFECTS;
    mCalled = false;
    onPause();

通过源码已经看得非常清晰了,我们直接对这一步(FragmentActivity#onPause() 方法)进行一个简单总结

  1. FragmentActivity#onPause() 中调用mFragments.dispatchPause()方法。
  2. dispatchPause() 方法执行过程中会将 FragmentManager#mCurState 的值由上一步的 Fragment.RESUMED 变为 Fragment.STARTED;并且将 Fragment#mState 的值改变,具体变化过程为: Fragment.RESUMED -> Fragment.AWAITING_ENTER_EFFECTS -> Fragment.STARTED,然后回调 Fragment#onPause() 方法。

FragmentActivity#onStop() 方法

源码追踪:

// FragmentActivity#onStop()
protected void onStop() 
    super.onStop();
    mFragments.dispatchStop();


// FragmentController#dispatchStop()
public void dispatchStop() 
    mHost.mFragmentManager.dispatchStop();


// FragmentManager#dispatchStop()
void dispatchStop() 
    mStopped = true;
    mNonConfig.setIsStateSaved(true);
    dispatchStateChange(Fragment.ACTIVITY_CREATED);

通过方法追踪: FragmentActivity#onStop() 方法后状态值传递的 Fragment.ACTIVITY_CREATED,通过调用最终方法肯定会走到 FragmentStateManager#moveToExpectedState() 方法,通过上面的分析我们应该都很熟悉了,所以中间的调用过程就不贴出来了。

computeExpectedState() 方法计算出的应该值是 Fragment.ACTIVITY_CREATED,和当前的 Fragment.STARTED 不一样,所以进入循环,这一步会经过一次循环:

case Fragment.ACTIVITY_CREATED:
    stop();
    break;

// FragmentStateManager#stop()
void stop() 
    mFragment.performStop();
    mDispatcher.dispatchOnFragmentStopped(mFragment, false);


// Fragment#performStop()
void performStop() 
    mState = ACTIVITY_CREATED;
    mCalled = false;
    onStop();

调用之后再次计算,还是一样的值,所以不在进入循环。
通过源码已经看得非常清晰了,我们直接对这一步(FragmentActivity#onStop() 方法)进行一个简单总结

  1. FragmentActivity#onStop() 中调用mFragments.dispatchPause()方法。
  2. dispatchPause() 方法执行过程中会将 FragmentManager#mCurState 的值由上一步的 Fragment.STARTED 变为 Fragment.ACTIVITY_CREATED;并且将 Fragment#mState 的值改变,具体变化过程为: Fragment.STARTED -> Fragment.ACTIVITY_CREATED,然后回调 Fragment#onStop() 方法。

FragmentActivity#onDestroy() 方法

源码追踪:

// FragmentActivity#onDestroy()
protected void onDestroy() 
    super.onDestroy();
    mFragments.dispatchDestroy();


// FragmentController#dispatchDestroy()
public void dispatchDestroy() 
    mHost.mFragmentManager.dispatchDestroy();


// FragmentManager#dispatchDestroy()
void dispatchDestroy() 
    mDestroyed = true;
    mNonConfig.setIsStateSaved(true);
    dispatchStateChange(Fragment.INITIALIZING);

通过方法追踪: FragmentActivity#onDestroy() 方法后状态值传递的 Fragment.INITIALIZING,通过调用最终方法肯定会走到 FragmentStateManager#moveToExpectedState() 方法,通过上面的分析我们应该都很熟悉了,所以中间的调用过程就不贴出来了。

computeExpectedState() 方法计算出的应该值是 Fragment.VIEW_CREATED,和当前的 Fragment.ACTIVITY_CREATED 不一样,所以进入循环,这一步会经过多次循环:

 case Fragment.AWAITING_EXIT_EFFECTS:
        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) 
            Log.d(TAG, "movefrom ACTIVITY_CREATED: " + mFragment);
        
        if (mFragment.mView != null) 
            // Need to save the current view state if not done already
            // by saveInstanceState()
            if (mFragment.mSavedViewState == null) 
                saveViewState();
            
        
        if (mFragment.mView != null && mFragment.mContainer != null) 
            SpecialEffectsController controller = SpecialEffectsController
                    .getOrCreateController(mFragment.mContainer,
                            mFragment.getParentFragmentManager());
            controller.enqueueRemove(this);
        
        mFragment.mState = Fragment.AWAITING_EXIT_EFFECTS;
        break;
    case Fragment.VIEW_CREATED:
        mFragment.mInLayout = false;
        mFragment.mState = Fragment.VIEW_CREATED;
        break;

这几次循环主要修改了 Fragment#mState 的值,并没有回调相关的生命周期方法,每一次循环结束后,都会继续进行计算当前应该的值然后判断,当Fragment#mState 的值变为 Fragment.VIEW_CREATED 后的下一次循环,计算出应该的值是 Fragment.INITIALIZING,与当前值不一样,所以继续循环:

case Fragment.CREATED:
    destroyFragmentView();
    mFragment.mState = Fragment.CREATED;
    break;
case Fragment.ATTACHED:
    destroy();
    break;
case Fragment.INITIALIZING:
    detach();
    break;

经历这几次循环时会调用多个和 Fragment 生命周期相关的方法,我们一个一个来看,他们时怎样修改各种状态的。

1. FragmentStateManager#destroyFragmentView()

// FragmentStateManager#destroyFragmentView()
void destroyFragmentView() 
	// 移除Fragment 的 View
    if (mFragment.mContainer != null && mFragment.mView != null) 
        mFragment.mContainer.removeView(mFragment.mView);
    
	// 调用 Fragment 的 performDestroyView() 方法
    mFragment.performDestroyView();

	// 将一些值设置为初始值
    mFragment.mContainer = null;
    mFragment.mView = null;
    mFragment.mViewLifecycleOwner = null;
    mFragment.mViewLifecycleOwnerLiveData.setValue(null);
    mFragment.mInLayout = false;


// 主要看 Fragment#performDestroyView()
void performDestroyView() 
	// 设置 mState 的值为 CREATE
    mState = CREATED;
    mCalled = false;
	// 回调 Fragment#onDestroyView() 方法
    onDestroyView();

通过FragmentStateManager#destroyFragmentView()方法的调用,会将 Fragment#mState 的值由 Fragment.VIEW_CREATED 变为 Fragment.CREATED,并回调 Fragment#onDestroyView() 方法。

2. FragmentStateManager#destroy()

// FragmentStateManager#destroy()
void destroy() 
    if (shouldDestroy) 
        mFragment.performDestroy();
     else 
        mFragment.mState = Fragment.ATTACHED;
    

判断是否需要调用 onDestory() 方法,如果不需要就直接修改状态值为 Fragment.ATTACHED,这里当然是需要的,我们看继续看一下 Fragment#performDestroy() 方法:

// Fragment#performDestroy()
void performDestroy() 
	// 修改 mState 的值
    mState = ATTACHED;
	// 初始化部分成员变量
    mCalled = false;
    mIsCreated = false;
	// 回调 Fragment 的 onDestroy() 方法
    onDestroy();

通过FragmentStateManager#performDestroy()方法的调用,会将 Fragment#mState 的值由 Fragment.CREATED 变为 Fragment.ATTACHED,并回调 Fragment#onDestroy() 方法。

3. FragmentStateManager#detach()

// FragmentStateManager#detach()
void detach() 
	// 调用 Fragment#performDetach() 方法
    mFragment.performDetach();

	// 修改 mState 的值
    mFragment.mState = Fragment.INITIALIZING;

	// 初始化部分成员变量
    mFragment.mHost = null;
    mFragment.mParentFragment = null;
    mFragment.mFragmentManager = null;


// Fragment#performDetach()
void performDetach() 
    mState = INITIALIZING;
    mCalled = false;

	// 回调 Fragment 的 onDetach() 方法
    onDetach();

    mLayoutInflater = null;

通过FragmentStateManager#detach()方法的调用,会将 Fragment#mState 的值由 Fragment.ATTACHED 变为 Fragment.INITIALIZING,并回调 Fragment#onDetach() 方法。

通过上面三个步骤的分析,对这一步(FragmentActivity#onDestroy() 方法)进行一个简单总结

  1. FragmentActivity#onDestroy() 中调用mFragments.dispatchPause()方法。
  2. dispatchPause() 方法执行过程中会将 FragmentManager#mCurState 的值由上一步的 Fragment.ACTIVITY_CREATED 先变为 Fragment.CREATED,再变为 Fragment.INITIALIZING
  3. 通过方法调用改变 Fragment#mState 的值,具体的变化为: Fragment.ACTIVITY_CREATED -> Fragment.AWAITING_EXIT_EFFECTS -> Fragment.VIEW_CREATED -> Fragment.CREATED -> Fragment.ATTACHED -> Fragment.INITIALIZING,然后回调 Fragment#onDestroyView()Fragment#onDestroy()Fragment#onDetach()方法。

总结

通过上一步的分析,我们已经知道了 Activity 的生命周期变化时怎样影响 Fragment 的对应的生命周期方法回调的,如果是 Fragment 嵌套 Fragment 的也是类似,是通过父 Fragment 影响子 Fragment 的,具体就是在 FragmentperformXxx() 方法中调用 FragmentManager 的方法,我们在这里随便看几个:

// Fragment#performDestroy()
void performDestroy() 
	// 通过FragmentManager改变子Fragment的状态和回调对应方法
    mChildFragmentManager.dispatchDestroy();

    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    mState = ATTACHED;
    mCalled = false;
    mIsCreated = false;
    onDestroy();


// Fragment#performDetach()
void performDetach() 
    mState = INITIALIZING;
    mCalled = false;
    onDetach();
    mLayoutInflater = null;

	// 通过FragmentManager改变子Fragment的状态和回调对应方法
    if (!mChildFragmentManager.isDestroyed()) 
        mChildFragmentManager.dispatchDestroy();
        mChildFragmentManager = new FragmentManagerImpl();
    

其他的就不再一个一个贴出来看了,都是一样或者类似的。

分析到这里,我们对于Fragment 的状态变化应该已经有了一个比较清晰的认知了。最后在来重新看一下上面的图

扩展:ActivityonCreateView() 方法的调用过程

《简书:Android inflate解析》《CSDN:Android inflate解析》文章中,我们知道LayoutInflater对象最终都是通过getSystemService()方法获取的。而这个方法关于 LayoutInflater 的实现是在 ContextThemeWrapper 类中,查看一下这个方法:

// 这个定义在 Context 中
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; 

// ContextThemeWrapper#getSystemService()
if (LAYOUT_INFLATER_SERVICE.equals(name)) 
    if (mInflater == null) 
        mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
    
    return mInflater;

return getBaseContext().getSystemService(name);

这个方法判断了我们所需要获取的 name,而 LAYOUT_INFLATER_SERVICE 正是我们获取LayoutInflater对象的名,所以会进入 if 条件中,LayoutInflater.from(getBaseContext()) 方法也是返回 LayoutInflater对象,但是这里再次调用了 LayoutInflater#cloneInContext() 方法,我们看一下这个方法的定义

public abstract LayoutInflater cloneInContext(Context newContext);

是一个抽象方法,那我们找一下它的实现方法,发现通过 Android studio 找不到,而通过debug的形式发现具体的类是 PhoneLayoutInflater,我们直接查找系统源码看一下这个类:

/**
 * @hide
 */
public class PhoneLayoutInflater extends LayoutInflater 
    
    private static final String[] sClassPrefixList = 
        "android.widget.",
        "android.webkit.",
        "android.app."
    ;

    @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException 
        for (String prefix : sClassPrefixList) 
            View view = createView(name, prefix, attrs);
            if (view != null) 
                return view;
            
        

        return super.onCreateView(name, attrs);
    

    public LayoutInflater cloneInContext(Context newContext) 
        return new PhoneLayoutInflater(this, newContext);
    

已省略部分代码,但是核心代码就是这么简单。发现这个类在源码中用 @hide 标记了,所以在Android studio中不能直接点击找到。他确实继承了 LayoutInflater 类,方法 cloneInContext() 就是返回了本类对象,那也就是说我们在使用 LayoutInflater 类时实际上使用的是 PhoneLayoutInflater 类。

调用 setContentView() 到调用 onCreateView() 过程

Activity#setContentView() -> PhoneWindow#setContentView() -> PhoneWindow#installDecor() -> PhoneWindow#generateLayout() -> DecorView#onResourcesLoaded() -> PhoneLayoutInflater#inflate()(实际是LayoutInflater,因为 PhoneLayoutInflater 中没有重写方法,内部多次调用重载,就不全部贴出来了) -> LayoutInflater#createViewFromTag()(同样内部多次调用重载,就不全部贴出来了) -> LayoutInflater#tryCreateView()

LayoutInflater#tryCreateView() 方法内容如下,在 xml 中的每一个Viewfragment标签实际也是一个View)创建完成都会调用,进而调用 onCreateView() 方法:

public final View tryCreateView(@Nullable View parent, @NonNull String name,
    @NonNull Context context,
    @NonNull AttributeSet attrs) 
    if (name.equals(TAG_1995)) 
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    

    View view;
    if (mFactory2 != null) 
        view = mFactory2.onCreateView(parent, name, context, attrs);
     else if (mFactory != null) 
        view = mFactory.onCreateView(name, context, attrs);
     else 
        view = null;
    

    if (view == null && mPrivateFactory != null) 
        view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    

    return view;

setPrivateFactory 的赋值是在 Activityattach() 方法中,有这样一行代码:

mWindow.getLayoutInflater().setPrivateFactory(this);

Activity 类是已经实现了 LayoutInflater.Factory2 接口的,所以直接将自身传递过来了。

其他的赋值的地方分别是 LayoutInflater#setFactory()LayoutInflater#setFactory2() 方法,LayoutInflater#setFactory() 方法已过时,没有传值的地方,LayoutInflater#setFactory2() 方法的赋值开始地方有两个:AppCompatDelegateImpl#installViewFactory() 方法 和 Fragment#getLayoutInflater() 方法。

  • AppCompatDelegateImplAppCompatActivity 类的委托类, AppCompatActivity 的相关操作交由其处理。

  • Fragment#getLayoutInflater()FragmentonCreateView() 提供类型为 LayoutInflater 的参数。

     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) 
         if (mContentLayoutId != 0) 
             return inflater.inflate(mContentLayoutId, container, false);
         
         return null;
     
    

    就是改回调方法的第一个参数。

以上是关于Fragment状态改变与管理的主要内容,如果未能解决你的问题,请参考以下文章

Fragment状态改变与管理

Fragment常见问题

Fragment常见问题

Fragment常见问题

FragmentViewPager中使用Fragment

FragmentViewPager中使用Fragment