当 Fragments 被解雇(不可见)时如何处理它们?

What to do with Fragments when they are dismissed (not visible)?

我正在用 3 个 Activity 重写一个蓝牙应用,只使用 1 个 Activity 和 3 个片段:

所以我现在有 4 个文件:

它几乎可以工作,但作为一个 Android 编程新手我不明白 - 当我显示其他片段时如何处理片段?

我是否应该删除 Fragment(并从 FragmentManager 中删除?)以进行垃圾收集?

或者我应该将这 3 个私有变量添加到 MainActivity.java 并重新使用它们(当用户向前和向后导航时)?

private MainFragment mMainFragment;
private SettingsFragment mSettingsFragment;
private ScanningFragment mScanningFragment;

或者 FragmentManager 是否以某种方式为我管理所有 3 个片段 - 无论它们是否可见?

这是我目前的代码(很简单,我一直调用replace())-

public class MainActivity extends Activity implements 
                                            MainListener, 
                                            SettingsListener, 
                                            ScanningListener,
                                            BleWrapperUiCallbacks {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        
        setContentView(R.layout.activity_main); // empty FrameLayout

        Fragment fragment = new MainFragment();
        getFragmentManager().beginTransaction()
            .replace(R.id.root, fragment, "main")
            .commit();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                Fragment fragment = new SettingsFragment();
                getFragmentManager().beginTransaction()
                    .addToBackStack(null)
                    .replace(R.id.root, fragment, "settings")
                    .commit();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    // implementing SettingsFragment.SettingsListener interface

    public void scanClicked() {
        // TODO how to stop indicator when returned?
        setProgressBarIndeterminateVisibility(true);

        String address = // get from shared preferences
        Fragment fragment = ScanningFragment.newInstance(address);
        getFragmentManager().beginTransaction()
            .addToBackStack(null)
            .replace(R.id.root, fragment, "scan")
            .commit();
    }

因为您是在片段管理器上调用 .replace(),所以它与调用 .remove() 本质上是一样的。根据 docs:

This is essentially the same as calling remove(Fragment) for all currently
added fragments that were added with the same containerViewId and 
then add(int, Fragment, String) with the same arguments given here.

因此您无需担心任何进一步的管理,因为它会为您处理(并被删除以释放资源)。这基本上意味着当显示一个时,另一个被删除。如果您要调用 .add() ,那么片段仍然会在后台使用资源,但您不必担心这一点,因为使用 .replace() 一次只允许一个片段存在。

如果我对你的问题理解正确的话,你使用后不需要调用任何方法销毁碎片。 Android OS 将采取他们。根据文档,当您用另一个片段替换片段时,将执行片段的 onStop() 方法,并记录为,

片段不可见。主机 activity 已停止,或者片段已从 activity 中删除但已添加到返回堆栈。停止的片段仍然存在(系统保留所有状态和成员信息)。但是,它不再对用户可见,如果 activity 被杀死,它将被杀死。

因此当 activity 被杀死时,片段将被 OS 杀死。在 activity 上线之前,片段对象将驻留在内存中。

美国东部时间:

因此,如果您以后想再次使用该片段,请按照文档的建议,

也像 activity,你可以使用 Bundle 保留片段的状态,以防 activity 的进程被杀死而你需要恢复重新创建 activity 时的片段状态。您可以在片段的 onSaveInstanceState() 回调期间保存状态,并在 onCreate()、onCreateView() 或 onActivityCreated() 期间恢复它。有关保存状态的详细信息,请参阅活动文档。

Should I just drop the Fragments (and remove from FragmentManager?) to be garbage collected?

无需执行任何其他操作。 FragmentManager 是负责 Fragments 生命周期的人。一旦您调用 replace(),FragmentManager 就会处理剩下的事情。如果需要,它会将片段保留在内存中,或释放它。

Or should I add these 3 private variables to MainActivity.java and reuse them (when the user navigates forwards and backwards)?

不,因为上面说的,所以不要这样做。

Or does FragmentManager somehow manage all 3 Fragment for me - regardless if they are visible or not?

是的,确实如此。例如,如果你有不可见的保留片段,创建一次就足够了,FragmentManager 会处理它,即使在配置更改期间重新创建 activity 时也会保留它。

如果您动态创建片段(据我所知,这就是您的情况),那么我建议也动态添加第一个片段。你可以这样做。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        
    setContentView(R.layout.activity_main); // empty FrameLayout

    if (savedInstanceState == null) {  // <- important
        Fragment fragment = new MainFragment();
        getFragmentManager().beginTransaction()
            .replace(R.id.root, fragment, "main")
            .commit();
    }
}

这将确保您不会在配置更改时复制 MainFragment,因为当 savedInstanceState 不为空时,FragmentManager 已经保留了您的片段实例。

Fragment 硬编码在 xml 中无法替换.... 但是当你打电话给 replace(..) 那么会发生什么?? 好吧,考虑一下你有 3 个片段 A,B,C 。在初级阶段 A 在 MainActivity 中初始化...现在你要从 A 调用 B 使用replace(....)。这意味着 A 将进入生命周期的 onPause(),onStop() 状态并且 B 将在 MainActivity 中初始化和调整...与 C那么B就是onPause() ,onStop()状态。如果你想重用 AB 那么你需要再次调用 ABC 使用 replace (..)。然后 AB 将重新初始化并且 C 转到 onPause(),onStop()。但还有另一种方法可以做到这一点

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
hide(A);
show(B);
ft.commit(); 

如果你使用上面的代码块,那么 A 仍然处于其生命周期的 运行 状态但不可见(只是从 UI 分离)。 . 因此,如果您再次需要这些片段,最好使用 hide()or show() 方法,因为它 更便宜 操作然后重新初始化片段。