简体   繁体   English

通过配置更改保留对象的静态模式

[英]Static pattern to retain objects through configuration changes

I am experienced enough to know about configuration changes. 我有足够的经验来了解配置更改。 I know how to save and restore the instance state of a View , Activity , Fragment or whatever, and I know that we should rely on that, rather than simply make objects static. 我知道如何保存和还原ViewActivityFragment或其他内容的实例状态,我知道我们应该依靠它,而不是简单地使对象静态化。

This question is about static ness of a field. 这个问题是关于一个领域的static性。 In "pure Java" declaring an object as static serves the purpose of having it shared among instances. 在“纯Java”中,将对象声明为static的目的是使对象在实例之间共享。 In the android system, declaring a static field is also a quick way to have it survive configuration changes, when our instance is recreated and its fields are cleared. 在android系统中,当重新创建实例并清除其字段时,声明static字段也是使它配置更改后仍然有效的一种快速方法。

Example: 例:

public class FooFragment extends Fragment {

    private static Object sObject;

    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            sObject = new Object();
            sObject.toString();
        } else {
            sObject.toString();
        }
    }

}

In this case, sObject can be initialized when the fragment is created , and just fetched when the fragment is recreated . 在这种情况下,可以在创建片段时初始化sObject ,而在重新创建片段时仅获取sObject Issues arise when our class is not static itself, is designed as a base class, or is an abstract class. 当我们的类本身不是静态的,被设计为基类或abstract类时,就会出现问题。

public abstract class BaseFragment extends Fragment {

    protected static Object sObject;
}

public class FooFragment extends BaseFragment {

    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            sObject = new Object();
            sObject.toString();
        } else {
            sObject.toString();
        }
    }
}

public class BarFragment extends BaseFragment {

    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            sObject = new Object();
            sObject.toString();
        } else {
            sObject.toString();
        }
    }
}

Now both FooFragment and BarFragment will access (and impredictably use) the same instance of sObject . 现在, FooFragmentBarFragment都将访问​​(并且不可更改地使用) sObject的相同实例。 That is the point of staticness, but is not what we want. 那是静态的要点,但不是我们想要的。 We want FooFragment and BarFragment to have their own sObject ; 我们希望FooFragmentBarFragment具有自己的sObject we just want it to survive configuration changes and platform-driven recreation. 我们只希望它能够承受配置更改和平台驱动的娱乐。

Question

How can I design the base class for this goal? 我如何为此目标设计基类?

This is what I came up with, but I'm sure there's something better. 这是我想出的,但是我敢肯定有更好的东西。

public abstract class BaseFragment extends Fragment {

    private static HashMap<String, Object> sObjects;

    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            Object o = new Object();
            sObjects.put(getClass().getName(), o);
        }
    }

    protected final Object getObject() {
        return sObjects.get(getClass().getName());
    }
}

Basically I keep a static map, indexing by child class name. 基本上,我会保留一个静态映射,并按子类名称进行索引。

Basically, you are implementing a global cache. 基本上,您正在实现全局缓存。 The fact that you happen to be using it for configuration changes is just a detail. 您碰巧将其用于配置更改的事实只是一个细节。 Hence, you need to consider the standard stuff for global caches. 因此,您需要考虑全局缓存的标准配置。 For example, consider HashMap<String, WeakReference<Object>> , so if you fail to remove the object from the HashMap due to a coding bug or unhandled exception, the long-term impact will be modest. 例如,考虑HashMap<String, WeakReference<Object>> ,因此,如果由于编码错误或未处理的异常而无法从HashMap删除对象,则长期影响将不大。

In terms of more Android-specific considerations, nothing that goes into that cache should be configuration-dependent, such as the value of a string culled from string resources. 考虑到更多特定于Android的注意事项,进入该缓存的任何内容都不应依赖于配置,例如从字符串资源中挑选出的字符串的值。 These need to be rebuilt, as the configuration change may affect their value. 这些都需要重建,因为配置更改可能会影响它们的价值。

Also, nothing that goes into that cache should be dependent upon the pre-configuration-change environment, such as views owned by the former activity, to help reduce memory leaks. 同样,进入该缓存的任何内容都不应依赖于配置前更改环境,例如前活动所拥有的视图,以帮助减少内存泄漏。

While you think that retained fragments are inappropriate, you might consider looking at their implementation, or the implementation of other global caches (eg, Picasso's image cache), for other ideas of what might befall you. 当您认为保留的片段不合适时,您可以考虑查看其实现,或其他全局缓存(例如Picasso的图像缓存)的实现,以了解可能会遇到的其他问题。

Your method will work if you have a small app. 如果您有一个小型应用程序,则您的方法将起作用。 However there are a couple of things that this can make difficult. 但是,有些事情可能会使这变得困难。

1. Memory footprint: Every static instance is retained even if you are not using that screen. 1.内存占用量:即使不使用该屏幕,每个静态实例也会保留。 As stated above, this is not a big deal if your application is small, but can cause issues if your application grows. 如上所述,如果您的应用程序很小,这并不是什么大问题,但是如果您的应用程序增长,可能会导致问题。

2. Testability: Testing static things is not fun :( 2.可测试性:测试静态事物并不是一件有趣的事情:(

There may be easier ways to achieve your goals. 可能会有更简单的方法来实现您的目标。

Option A Load from DB. 选项A从数据库加载。 Basically save as little state as possible (maybe ID of data your are looking at) and load data on orientation change. 基本上保存尽可能少的状态(可能是您正在查看的数据的ID),并在方向更改时加载数据。

Option B Make saving state easier with a library such as IcePick You can save primitives, or create customer converters for objects (eg Use GSON to convert object to JSON). 选项B使用IcePick这样的库使保存状态变得更容易。您可以保存基元,或为对象创建客户转换器(例如,使用GSON将对象转换为JSON)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM