-
简书
-
优快云
Fragment(二)状态改变与管理
通过这篇博客,我们能知道以下问题:
Fragment
的mState
变化过程FragmentManager
的mCurState
变化过程
在《Fragment(一)从源码角度看add和replace过程》中,我们知道了Fragment
使用add
和replace
加载到页面的基本过程,最后是根据状态判断,然后调用对应的方法,但是并没有详细说明状态的变化过程,这篇文章我们就主要来说说这个过程。在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 的组成》 、 《优快云: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
构造)简单总结:
- 启动一个
FragmentActivity
- 在
FragmentActivity
中创建类型为FragmentController
的成员变量mFragments
,创建这个对象的需要一个HostCallbacks
对象,所以也会创建,在HostCallbacks
的父类FragmentHostCallback
中有一个类型为FragmentManager
的成员变量mFragmentManager
也会创建(具体为FragmentManager
的子类FragmentManagerImpl
),- 调用构造和内部的
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()
方法,在这个方法的调用过程中会回调到 FragmentActivity
的 onCreateView()
方法(这个调用过程在文章底部 “扩展:Activity
中 onCreateView()
方法的调用过程” 可以查看),在这个方法中又会调用到 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);
}
接着调用mFragments
的onCreateView()
方法,实际就是 FragmentController#onCreateView()
:
// FragmentController#onCreateView()
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
return mHost.mFragmentManager