为什么我会想要`setRetainInstance(false)`? - 或 - 处理设备旋转的正确方法

Tim*_*mmm 16 lifecycle android rotation fragment android-activity

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

在Android 4中,您可以调用setRetainInstance(true)on Fragment以便在配置更改(这基本上意味着设备旋转)时,Fragment不会销毁java对象并且不会创建它的新实例.也就是说,保留了实例.

由于您不必处理和捆绑所有数据,因此它可以传递给新的(或)实例,只是再次进行非捆绑,因此这比Android 1-3更加理智且不那么令人愤怒.这基本上是你期望发生的事情,并且可以说它应该从一开始就如何起作用.onRetainNonConfigurationStateInstance()FragmentActivityActivity

通过setRetainInstance(true)视图也可以onCreateView()像您期望的那样在旋转时重新创建(被调用).我假设(未测试)资源解析(layoutvs 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Com*_*are 13

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

并更改区域设置,更改SIM卡,更改默认字体大小,插入或移除外部键盘,将设备放入底座或从中移除等等.

你不必处理onRetainNonConfigurationState()

那是onRetainNonConfigurationInstance().

捆绑所有数据,以便它可以传递给新的Fragment(或Activity)实例,只是再次进行非捆绑

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

我假设(未测试)资源分辨率(布局与布局 - 土地)有效.

正确.

有没有理由为什么你真的希望你的片段被无意义地摧毁并在轮换时重新创建?

当然.

如您所知,所有小部件都被重新创建,因此与小部件绑定的数据成员不仅无需保留.除非你以null某种方式专门重置那些保留片段,直到onCreateView()再次调用,这些数据成员将保留旧的小部件,这将保留旧活动实例,这将阻止旧活动实例被垃圾收集.onCreateView()在重新显示片段之前不会调用AFAIK,这可能不会持续很长时间(片段不在新方向中使用,或者片段用于ViewPager用户访问的某个页面)旧的方向,但没有重新审视新的方向等).这意味着保留的片段可以将旧的活动对象保持很长一段时间.取决于活动可能保留的其他内容(例如,大型Bitmap对象),这可能是坏的.

类似地,在配置更改后可能会或可能不会使用该片段的大数据本身保留的片段是不应保留的片段.

此外,将存在片段,其根本不需要保留(例如,所有数据都填充Loaders,已经知道配置更改并适当地处理它们).

等等.

对于垃圾收集问题,未保留的默认片段是最安全的操作过程.你可以选择保留一些片段,但是有责任确保你不要因为这样做而搞砸自己.


zmb*_*mbq 0

我不知道第一个问题的答案。从一开始就应该如此。我猜谷歌的某个人认为他们提出这个方案真的很聪明。

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

  • 修复某些东西会激怒开发人员,因为他们已经习惯了它被破坏。严重地? (4认同)