为什么我会永远想`setRetainInstance(假)`? - 或 - 正确的方式来处理设备旋转 [英] Why would I ever want `setRetainInstance(false)`? - Or - The correct way to handle device rotation

查看:211
本文介绍了为什么我会永远想`setRetainInstance(假)`? - 或 - 正确的方式来处理设备旋转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请纠正我,如果我错了,在任何这一点。这是一种澄清的问题,因为我还没有看到它明确写入任何地方。

在的Andr​​oid 4.0,你可以叫 setRetainInstance(真)片段这样的配置更改(其中主要是指设备旋转),在片段 Java对象不被破坏,而不是创造了一个新的实例。即,该实例被保留

这是比Android的1-3更理智,少真气,因为你没有处理 onRetainNonConfiguration <打击> 国家 实例()并捆绑了所有数据,因此它可以被传递到新的片段(或活动)的实例只能再次分解开来。它基本上是你所期望的事情发生了,按理说它应该如何从一开始就工作了活动秒。

使用 setRetainInstance(真)视图也重新创建( onCreateView()的叫法)的旋转,你期望的那样。我认为(未测试),该资源分辨率(布局 VS 布局陆)的作品。

所以我的问题是双重的:

  1. 为什么不是这样用活动从一开始。
  2. 为什么这不就是默认?是否有过任何理由,为什么你的将会的其实是想你的片段被白白破坏并重新创建轮换?因为我想不出任何。

修改

要澄清我会怎么做:

 类MyFragment扩展片段
{
    //所有数据。
    字符串mDataToDisplay;
    // 等等。

    //所有的意见。
    TextView的mViewToDisplayItIn;
    // 等等。

    @覆盖
    公共无效的onCreate(包savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setRetainInstance(真正的);
        mDataToDisplay = readFromSomeFileOrWhatever(); //忽略线程问题现在。
    }

    @覆盖
    公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,捆绑savedInstanceState)
    {
        返回inflater.inflate(R.layout.my_fragment,集装箱,假);
    }

    @覆盖
    公共无效onViewCreated(查看视图,捆绑savedInstanceState)
    {
        //此时如果mViewToDisplayItIn不空,旧的将被GC'd。
        mViewToDisplayItIn = view.findViewById(R.id.the_text_view);
        mViewToDisplayItIn.setText(mDataToDisplay);
    }

    //可选:
    @覆盖
    公共无效onDestroyView()
    {
        //所有视图(和活动)被GC'd。
        mViewToDisplayItIn = NULL;
    }
}
 

解决方案
  

这样的配置变化(基本上是指设备旋转)

和不断变化的语言环境,改变SIM卡,更改默认字体大小,插入或删除外接键盘,把设备在码头或同一取出等。

  

您不必处理onRetainNonConfigurationState()

这是 onRetainNonConfigurationInstance()

  

只是捆绑了所有数据,因此它可以被传递到新的片段(或活动)的实例再次分解开来

您的数据应该已经被捆绑(例如,私有静态内部类的实例),因此不需要进行捆绑或分拆。此外,经常不应该是所有的数据,除非你的内存泄漏的风扇。

  

和我认为(未测试),该资源分辨率(布局VS布局土地)的作品。

正确的。

  

是否有过任何理由,你为什么会真的想你的片段被白白破坏并重新创建轮换?

当然。

正如您注意,所有部件都重新创建,所以数据成员捆绑到小部件,不仅没有必要保留。除非你专门重置那些在保留片段不知何故,直到 onCreateView()被再次调用,这些数据成员将守住旧的部件,这将守住旧的活动实例,这将$,从旧的活动实例被垃圾收集p $ pvent。 AFAIK, onCreateView()不会被调用,直到片段将被重新显示,这可能不是在相当一段时间(该片段没有在新的使用方向,或者该片段是对一些页面在 ViewPager 该用户访问过的旧取向,但不重新访问在新的方向等)。这意味着,该保留的片段可以保留旧的活动对象周围的相当长一段时间。根据什么的其他的这一活动可能扶住(例如,大位图对象),这可能是坏的。

类似地,一个片段,该片段本身保存到大型数据,其中该碎片可以或不可以被配置改变之后使用的,是一个不应该被保留。

此外,将会有根本没有什么需要被保留下来(例如,所有的数据都居住着装载机,它们已经意识到了配置更改和处理这些片段适当)。

等等。

片段的默认暂时不保留是最安全的操作过程中,对于垃圾回收的问题。你可以选择到有一些片段被保留,但随后的责任是对的的,以确保你不这样做的话拧自己。

Please correct me if I'm wrong on any of this. This is a kind of clarifying question since I haven't seen it explicitly written anywhere.

In Android 4, you can call setRetainInstance(true) on a Fragment so that on configuration changes (which basically means device rotation), the Fragment java object isn't destroyed and a new instance of it isn't created. That is, the instance is retained.

This is much more sane and less infuriating than in Android 1-3 since you don't have to deal with onRetainNonConfigurationStateInstance() and bundle up all your data so it can be passed to the new Fragment (or Activity) instance only to be unbundled again. It's basically what you would expect to happen, and arguably how it should have worked for Activitys from the beginning.

With setRetainInstance(true) the view is also recreated (onCreateView() is called) on rotation as you would expect. And I assume (not tested) that resource resolution (layout vs layout-land) works.

So my question is two-fold:

  1. Why wasn't it like this with Activities from the beginning.
  2. Why isn't this the default? Is there ever any reason why you would actually want your Fragment to be pointlessly destroyed and recreated on rotation? Because I can't think of any.

Edit

To clarify how I would do it:

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

解决方案

so that on configuration changes (which basically means device rotation)

And changing locale, changing SIMs, changing default font size, plugging in or removing an external keyboard, putting the device in a dock or removing it from same, etc.

you don't have to deal with onRetainNonConfigurationState()

That's onRetainNonConfigurationInstance().

bundle up all your data so it can be passed to the new Fragment (or Activity) instance only to be unbundled again

Your data should already be "bundled" (e.g., instance of private static inner class) and therefore it would not need to be "bundled" or "unbundled". Also, it frequently should not be "all your data", unless you are a fan of memory leaks.

And I assume (not tested) that resource resolution (layout vs layout-land) works.

Correct.

Is there ever any reason why you would actually want your Fragment to be pointlessly destroyed and recreated on rotation?

Sure.

As you note, all widgets are recreated, so data members tied to widgets are not only unnecessary to retain. Unless you specifically reset those to null on a retained fragment somehow, until onCreateView() is called again, those data members would hold onto the old widgets, which would hold onto the old activity instance, which would prevent that old activity instance from being garbage collected. AFAIK, onCreateView() is not going to be called until the fragment is going to be redisplayed, which may not be for quite some time (the fragment is not used in the new orientation, or the fragment is for some page in a ViewPager that the user visited in the old orientation but does not revisit in the new orientation, etc.). That means that the retained fragment may keep the old activity object around for a substantial period of time. Depending on what else that activity may have held onto (e.g., large Bitmap objects), that could be bad.

Similarly, a fragment that itself holds onto large data, where that fragment may or may not be used after the configuration change, is one that should not be retained.

Also, there will be fragments that simply have nothing needing to be retained (e.g., all data is populated by Loaders, which are already aware of configuration changes and handle them appropriately).

And so on.

A default of fragments being not retained is the safest course of action, with respect to garbage collection issues. You can opt into having some fragments be retained, but then the onus is on you to make sure that you are not screwing yourself by doing that.

这篇关于为什么我会永远想`setRetainInstance(假)`? - 或 - 正确的方式来处理设备旋转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆