Activity 专题- Activity 如何托管 Fragment 的生命周期解析
或者你使用过 DialogFragment,DialogFragment 是一种特殊的 Fragment ,这里简单介绍下原理,其实就是 Fragment 里面封装了一个 Dialog,
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
Dialog mDialog;
FragmentHostCallback mHost;
//显示这个 Fragment 调用这个方法
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
// 这里会创建一个默认的 dialog
@override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
if (!mShowsDialog) {//如果不显示 dialog,则走普通 Fragment 流程
return super.getLayoutInflater(savedInstanceState);
}
//以下是创建 dialog 的代码
mDialog = onCreateDialog(savedInstanceState);
switch (mStyle) {
case STYLE_NO_INPUT:
mDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
// fall through...
case STYLE_NO_FRAME:
case STYLE_NO_TITLE:
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
if (mDialog != null) {
return (LayoutInflater)mDialog.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
//这里的 mHost 对象就是 Fragment 宿主的 Activity
return (LayoutInflater) mHost.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
//这里会对 Dialog 进行 setContentView() 操作,设置视图,getView() 方法返回的就是 你实现的 onCreateView() 返回的View
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (!mShowsDialog) {
return;
}
View view = getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException(
"DialogFragment can not be attached to a container view");
}
mDialog.setContentView(view);
}
final Activity activity = getActivity();
if (activity != null) {
mDialog.setOwnerActivity(activity);
}
mDialog.setCancelable(mCancelable);
if (!mDialog.takeCancelAndDismissListeners("DialogFragment", this, this)) {
throw new IllegalStateException(
"You can not set Dialog's OnCancelListener or OnDismissListener");
}
if (savedInstanceState != null) {
Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
if (dialogState != null) {
mDialog.onRestoreInstanceState(dialogState);
}
}
}
// onStar() 时会调用 Dialog.show(),也就是 Activity.onStart() 的时候
@Override
public void onStart() {
super.onStart();
if (mDialog != null) {
mViewDestroyed = false;
mDialog.show();
}
}
// onStop() 的时候会关闭 Dialog
@Override
public void onStop() {
super.onStop();
if (mDialog != null) {
mDialog.hide();
}
}
}
大致的流程如下:
- DialogFragment 内部在 getLayoutInflater() 方法的时候会创建一个 Dialog,也就是 Fragment 初始化的时候。
- DialogFragment 在 onActivityCreated() 的时候,会给 Dailog 设置视图,调用了 getView() 方法,也就是返回 onCreateView() 的返回值。
- onStart() 的时候会调用 dialog.show()
- onStop() 的时候会调用 dialog.hide()
还可以通过重写 onCreateDialog() ,可以实现展示自定义的 Dialog.
public Dialog onCreateDialog(Bundle savedInstanceState) {
return super.onCreateDialog(savedInstanceState);
}
这种 Dialog 的好处就是,能够通过 Fragment 监听到 Activity 的生命周期回调,从而保证,在 Activity 关闭的时候,能够确保 Dialog 是关闭的,而不会造成因 Dialog 未关闭,导致 Activity 泄漏。
Activity 如何托管 Fragment 的生命周期
除了上面提到的,DialogFragment,同样在 Glide 框架里面,也是巧妙的用了这一个原理,使得基于 Activity Context 的 Rquest 能够自动伴随 Activity 的生命周期取消,从而提高了执行效率。
这里主要通过 FragmentHostCallback 实现 Fragment 追随Activity 的生命周期,同时也是实现了 Activity 调度 Fragment 的生命周期。
先来看看这个类:
/**
* Integration points with the Fragment host.
* <p>
* 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.
*/
public abstract class FragmentHostCallback<E> extends FragmentContainer {
/**
* Called when a {@link Fragment} is being attached to this host, immediately
* after the call to its {@link Fragment#onAttach(Context)} method and before
* {@link Fragment#onCreate(Bundle)}.
*/
public void onAttachFragment(Fragment fragment) {
}
/**
* Return a {@link LayoutInflater}.
* See {@link Activity#getLayoutInflater()}.
*/
public LayoutInflater onGetLayoutInflater() {
return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
void doLoaderStart() {
if (mLoadersStarted) {
return;
}
mLoadersStarted = true;
if (mLoaderManager != null) {
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
}
mCheckedForLoaderManager = true;
}
void doLoaderStop(boolean retain) {
mRetainLoaders = retain;
if (mLoaderManager == null) {
return;
}
if (!mLoadersStarted) {
return;
}
mLoadersStarted = false;
if (retain) {
mLoaderManager.doRetain();
} else {
mLoaderManager.doStop();
}
}
void doLoaderDestroy() {
if (mLoaderManager == null) {
return;
}
mLoaderManager.doDestroy();
}
}
这个类的官方 API 解释就是,Fragment 可以寄宿在任何对象上,例如 Activity。为了管理 Fragment,需要实现 FragmentHostCallback 方法,重载一些关键的方法,实现对 Fragment 的管理。
这里列举一些关键的方法,具体的细节可以自行查看源码。
然后在 Activity 中,我们可以看到这样一些代码:
public class Activity {
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
@CallSuper
protected void onStart() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
mCalled = true;
mFragments.doLoaderStart();
getApplication().dispatchActivityStarted(this);
}
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (!"fragment".equals(name)) {
return onCreateView(name, context, attrs);
}
return mFragments.onCreateView(parent, name, context, attrs);
}
final void performDestroy() {
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
}
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
}
@Override
public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
Activity.this.dump(prefix, fd, writer, args);
}
@Override
public boolean onShouldSaveFragmentState(Fragment fragment) {
return !isFinishing();
}
@Override
public LayoutInflater onGetLayoutInflater() {
final LayoutInflater result = Activity.this.getLayoutInflater();
if (onUseFragmentManagerInflaterFactory()) {
return result.cloneInContext(Activity.this);
}
return result;
}
@Override
public boolean onUseFragmentManagerInflaterFactory() {
// Newer platform versions use the child fragment manager's LayoutInflaterFactory.
return getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
}
@Override
public Activity onGetHost() {
return Activity.this;
}
@Override
public void onInvalidateOptionsMenu() {
Activity.this.invalidateOptionsMenu();
}
@Override
public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode,
Bundle options) {
Activity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}
@Override
public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
int extraFlags, Bundle options) throws IntentSender.SendIntentException {
if (mParent == null) {
startIntentSenderForResultInner(intent, fragment.mWho, requestCode, fillInIntent,
flagsMask, flagsValues, options);
} else if (options != null) {
mParent.startIntentSenderFromChildFragment(fragment, intent, requestCode,
fillInIntent, flagsMask, flagsValues, extraFlags, options);
}
}
@Override
public void onRequestPermissionsFromFragment(Fragment fragment, String[] permissions,
int requestCode) {
String who = REQUEST_PERMISSIONS_WHO_PREFIX + fragment.mWho;
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(who, intent, requestCode, null);
}
@Override
public boolean onHasWindowAnimations() {
return getWindow() != null;
}
@Override
public int onGetWindowAnimations() {
final Window w = getWindow();
return (w == null) ? 0 : w.getAttributes().windowAnimations;
}
@Override
public void onAttachFragment(Fragment fragment) {
Activity.this.onAttachFragment(fragment);
}
@Nullable
@Override
public View onFindViewById(int id) {
return Activity.this.findViewById(id);
}
@Override
public boolean onHasView() {
final Window w = getWindow();
return (w != null && w.peekDecorView() != null);
}
}
}
也就是 Activity 中的内部类HostCallbacks 继承了 FragmentHostCallback ,实现了一些关键的方法,用于在 Activity 的生命周期中,回调Fragment 中对应的方法。
最终会通过 FragmentController 分发给 Fragment,我们以 onDestory() 行为为例子,进行分析:
step1:
Activity.performDestory()
final void performDestroy() {
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
}
step2:
FragmentController.dispatchDestory()
public void dispatchDestroy() {
mHost.mFragmentManager.dispatchDestroy();
}
也就是会回调 Host 的 FragmentManager 里的方法,那么 Activity 里面的 FragmentManager 对象,其实就是 FragmentManagerImpl 类。
step3:
FragmentManagerImpl.dispatchDestory()
public void dispatchDestroy() {
mDestroyed = true;
execPendingActions();
mExecutingActions = true;
moveToState(Fragment.INITIALIZING, false);
mExecutingActions = false;
mHost = null;
mContainer = null;
mParent = null;
}
这里进行一系列标记清楚,并且把 mHost 等引用置为 null,解除了和 Activity 之间的引用,里面还调用了 moveToState() 方法,这里对 Fragment 的状态等进行了重置,具体细节比较复杂,就不进行分析,可以理解的是,这是 Fragment 回到了,还没创建的状态。
好了,到这里 Fragment 怎么被 Activity 寄管的,基本也就清晰了。