Activity 专题- Activity 托管 Fragment 的生命周期解析

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();
        }
    }
}

大致的流程如下:

  1. DialogFragment 内部在 getLayoutInflater() 方法的时候会创建一个 Dialog,也就是 Fragment 初始化的时候。
  2. DialogFragment 在 onActivityCreated() 的时候,会给 Dailog 设置视图,调用了 getView() 方法,也就是返回 onCreateView() 的返回值。
  3. onStart() 的时候会调用 dialog.show()
  4. 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 寄管的,基本也就清晰了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值