Activity对DialogFragment的管理

2020-04-09  本文已影响0人  Fakecoder_Sunis

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
大致的流程如下:

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);
}
1
2
3
这种 Dialog 的好处就是,能够通过 Fragment 监听到 Activity 的生命周期回调,从而保证,在 Activity 关闭的时候,能够确保 Dialog 是关闭的,而不会造成因 Dialog 未关闭,导致 Activity 泄漏。

Activity 如何托管 Fragment 的生命周期
除了上面提到的,DialogFragment,同样在 Glide 框架里面,也是巧妙的用了这一个原理,使得基于 Activity Context 的 Rquest 能够自动伴随 Activity 的生命周期取消,从而提高了执行效率。

这里主要通过 FragmentHostCallback 实现 Fragment 追随Activity 的生命周期,同时也是实现了 Activity 调度 Fragment 的生命周期。

先来看看这个类:

/**

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
这个类的官方 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);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
也就是 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();
}
}
1
2
3
4
5
6
7
8
9
10
step2:

FragmentController.dispatchDestory()

public void dispatchDestroy() {
mHost.mFragmentManager.dispatchDestroy();
}
1
2
3
也就是会回调 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;
}
1
2
3
4
5
6
7
8
9
10
这里进行一系列标记清楚,并且把 mHost 等引用置为 null,解除了和 Activity 之间的引用,里面还调用了 moveToState() 方法,这里对 Fragment 的状态等进行了重置,具体细节比较复杂,就不进行分析,可以理解的是,这是 Fragment 回到了,还没创建的状态。

上一篇下一篇

猜你喜欢

热点阅读