保持对SharedPreferences及其编辑器的静态引用是否安全?

Sta*_*tan 10 android android-context

我要做的事情如下:

private static SharedPreferences sharedPreferencesInstance;
public static SharedPreferences getSharedPreferences(final Context context){
    if (context==null)
        return sharedPreferencesInstance;
    if (sharedPreferencesInstance == null)
        sharedPreferencesInstance = context.getApplicationContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
    return sharedPreferencesInstance;
}

private static SharedPreferences.Editor sharedPreferencesEditorInstance;
public static SharedPreferences.Editor getSharedPreferencesEditor(final Context context){
    if (context==null)
        return sharedPreferencesEditorInstance;
    if (sharedPreferencesEditorInstance == null)
        sharedPreferencesEditorInstance = context.getApplicationContext().getSharedPreferences("prefs", Context.MODE_PRIVATE).edit();
    return sharedPreferencesEditorInstance;
}
Run Code Online (Sandbox Code Playgroud)

但在Context泄漏的意义上它是否安全?

Mat*_*ley 21

要以权威方式回答问题,将实例存储为静态引用是安全的SharedPreferences.根据javadocs它是一个单例,因此它的源代码getSharedPreferences已经是一个静态引用.

存储它是不安全的,SharedPreferences.Editor因为两个线程可能同时操作同一个编辑器对象.当然,如果您碰巧已经这样做,那么造成的损害相对较小.而是在每个编辑方法中获取编辑器的实例.

强烈建议使用Application对象的静态引用,而不是Context每次获取都传入对象.Application无论如何,你的类的所有实例都是单个进程的单例,并且传递Context对象通常是不好的做法,因为它往往会通过引用保持导致内存泄漏,并且不必要地冗长.

最后,要回答未提出的问题,如果你应该懒惰地加载或贪婪地初始化对静态的引用SharedPreferences,你应该懒惰地加载静态getter方法.它可以工作,贪婪地初始化的参考final static SharedPreferences sReference = YourApplication.getInstance().getSharedPreferences()依据类进口的链条,但它是太容易的类装载器初始化参考之前Application已叫onCreate(在那里你会初始化YourApplication参考),造成空值指针异常.综上所述:

class YourApplication {
    private static YourApplication sInstance;

    public void onCreate() {
        super.onCreate();
        sInstance = this;
    }
    public static YourApplication get() {
        return sInstance;
    }
}

class YourPreferencesClass {
    private static YourPreferencesClass sInstance;
    private final SharedPreferences mPrefs;

    public static YourPreferencesClass get() {
        if (sInstance == null)
            sInstance = new YourPreferencesClass();
        return sInstance;
    }

    private final YourPreferencesClass() {
        mPrefs = YourApplication.get().getSharedPreferences("Prefs", 0);
    }

    public void setValue(int value) {
        mPrefs.edit().putInt("value", value).apply();
    }

    public int getValue() {
        return mPrefs.getInt("value", 0);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将使用静态可用的首选项类:

YourPreferencesClass.get().setValue(1);
Run Code Online (Sandbox Code Playgroud)

关于线程安全性和内存可观察性的最后一句话.一些精明的观察者可能会注意到它YourPreferencesClass.get()不同步,因此很危险,因为两个线程可能会初始化两个不同的对象.但是,您可以安全地避免同步.正如我之前提到的,getSharedPreferences已经返回一个静态引用,因此即使在极少数情况下sInstance被设置两次,SharedPreferences也会使用相同的底层引用.关于静态实例YourApplication.sInstance,没有同步或volatile关键字也是安全的.之前运行的应用程序中没有用户线程YourApplication.onCreate,因此为新创建的线程定义的before-before关系确保静态引用对于可能访问所述引用的所有未来线程都是可见的.