【问题标题】:Why would I ever want `setRetainInstance(false)`? - Or - The correct way to handle device rotation为什么我会想要`setRetainInstance(false)`? - 或 - 处理设备旋转的正确方法
【发布时间】:2012-09-11 16:30:44
【问题描述】:

如果我在这方面有任何错误,请纠正我。这是一个澄清问题,因为我没有看到它在任何地方明确写过。

在 Android 4 中,您可以在 Fragment 上调用 setRetainInstance(true),以便在配置更改(这基本上意味着设备轮换)时,Fragment java 对象不会被破坏,并且它的新实例不会创建的。即保留实例。

这比在 Android 1-3 中要理智得多,也不那么令人恼火,因为您不必处理 onRetainNonConfigurationStateInstance() 并将所有数据捆绑起来可以传递给新的Fragment(或Activity)实例,但只能再次解绑。这基本上是您期望发生的事情,并且可以说它应该如何从一开始就为Activitys 工作。

使用setRetainInstance(true),视图也会按照您的预期在旋转时重新创建(调用onCreateView())。我假设(未经测试)资源解析(layout vs layout-land)有效。

所以我的问题有两个:

  1. 为什么一开始不是Activities
  2. 为什么这不是默认值?您是否有任何理由真正希望您的Fragment 被无意义地销毁并在轮换时重新创建?因为我想不出来。

编辑

澄清我将如何做到这一点:

class MyFragment extends Fragment
{
    // All the data.
    String mDataToDisplay;
    // etc.

    // All the views.
    TextView mViewToDisplayItIn;
    // etc.

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        mDataToDisplay = readFromSomeFileOrWhatever(); // Ignoring threading issues for now.
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.my_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState)
    {
        // At this point if mViewToDisplayItIn was not null, the old one will be GC'd.
        mViewToDisplayItIn = view.findViewById(R.id.the_text_view);
        mViewToDisplayItIn.setText(mDataToDisplay);
    }

    // Optionally:
    @Override
    public void onDestroyView()
    {
        // All the view (and activity) to be GC'd.
        mViewToDisplayItIn = null;
    }
}

【问题讨论】:

    标签: android android-activity rotation fragment lifecycle


    【解决方案1】:

    以便配置更改(这基本上意味着设备旋转)

    以及更改区域设置、更改 SIM 卡、更改默认字体大小、插入或移除外部键盘、将设备放入扩展坞或从中移除等。

    你不必处理 onRetainNonConfigurationState()

    那是onRetainNonConfigurationInstance()

    捆绑所有数据,以便将其传递给新的 Fragment(或 Activity)实例,然后再次解除捆绑

    您的数据应该已经“捆绑”(例如,私有静态内部类的实例),因此不需要“捆绑”或“取消捆绑”。此外,它通常不应该是“你所有的数据”,除非你是内存泄漏的粉丝。

    我假设(未经测试)资源解析(布局与布局土地)有效。

    正确。

    您是否真的希望您的 Fragment 被无意义地销毁并在轮换时重新创建?

    当然。

    正如您所注意到的,所有小部件都会重新创建,因此与小部件绑定的数据成员不仅不需要保留。除非您以某种方式在保留的片段上专门将它们重置为null,直到再次调用onCreateView(),这些数据成员将保留旧的小部件,这些小部件将保留旧的活动实例,这将阻止旧的活动实例被垃圾收集。 AFAIK,onCreateView() 不会被调用,直到片段将被重新显示,这可能不会在很长一段时间内(片段未用于新方向,或者片段用于 @987654325 中的某个页面@ 用户以旧方向访问但未在新方向重新访问等)。这意味着保留的片段可能会在相当长的一段时间内保留旧的活动对象。取决于该活动可能持有的else(例如,大型Bitmap 对象),这可能很糟糕。

    同样,一个片段本身包含大数据,在配置更改后可能会或可能不会使用该片段,是不应该保留的片段。

    此外,还会有一些片段根本不需要保留(例如,所有数据都由Loaders 填充,它们已经知道配置更改并适当地处理它们)。

    等等。

    就垃圾回收问题而言,默认不保留片段是最安全的做法。您可以选择保留一些片段,但是有责任确保您不会因为这样做而搞砸自己。

    【讨论】:

    • 很多积分! 1. 我从来没有在私有静态内部类中看到任何片段捆绑数据,而且我在谷歌的代码上工作了很多。你能提供一个他们推荐的链接吗? 2. 当onCreateView() 再次调用时,视图成员变量(即mProgressView 等)肯定会被覆盖吗?或者您可以在onDestroyView()=null 他们。 3.“普通”Activitys和Fragments隐藏后可以保留很长时间,所以我看不出有什么不同。为回复干杯!
    • @Timmmm:1. 他们不会——我会。 :-) 2. 你需要记住null他们在onDestroyView()。 3. 但是在这种情况下,活动被销毁,而不是隐藏,但由于来自保留片段的引用而无法被 GC。
    【解决方案2】:

    我不知道第一个问题的答案。 应该从一开始就是这样。我猜 Google 的某个人认为他们真的很聪明地想出了这个方案。

    然而,第二个问题要容易得多。这不是默认设置,因为这不是 Android 开发所期望的。 Android 开发人员知道实例会在轮换时死掉并期待它。更改默认设置会让很多开发人员非常生气。

    【讨论】:

    • 修复某些东西会惹恼开发人员,因为他们已经习惯了它被破坏。认真的吗?
    • 改变现有组件的行为肯定会惹恼开发人员。不是那些听到这个变化的热情的开发者,而是那些突然发现他们的代码不再适用于新版本的 SDK 的“随机”开发者会生气。
    • 我认为你们在谈论不同的事情:让Activity 默认情况下这样工作显然是不可能的 - 那列火车很久以前就航行了。但是他们最近才添加了片段和setRetainInstance();他们本可以将其设为片段的默认值。我的猜测是选择一致性而不是让一半的 API 理智,一半疯狂。
    • Fragments 是在 Android 3.0 中添加的,在 4.0 出来之前的好几年。
    • Android 3.0 才 18 个月大!也许你的意思是“好几个月”? :-)
    猜你喜欢
    • 1970-01-01
    • 2019-05-30
    • 2012-02-24
    • 2011-11-18
    • 1970-01-01
    • 2021-12-10
    • 2014-10-24
    • 2022-08-15
    • 1970-01-01
    相关资源
    最近更新 更多