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

时间:2014-03-20 20:35:13

标签: 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;
}

但是上下文泄漏的含义是否安全?

4 个答案:

答案 0 :(得分:21)

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

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

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

最后,如果您应该懒惰地加载或贪婪地初始化对静态SharedPreferences的引用,要回答未提出的问题,您应该懒惰地加载静态getter方法。 可能根据类导入链贪婪地初始化带有final static SharedPreferences sReference = YourApplication.getInstance().getSharedPreferences()的引用,但是类加载器在{{1}之前初始化引用太容易了已调用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);
    }
}

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

答案 1 :(得分:1)

我认为这是安全的。我总是使用" KeyStoreController"使用对SharedPreferences对象的静态引用(单例)。我建议你使用Application上下文,而不是每次都传递一个上下文。这是我的代码示例:

public class KeyStoreController{


private static KeyStoreController singleton = null;
private SharedPreferences preferences = null;

private KeyStoreController(Context c){
    preferences = PreferenceManager.getDefaultSharedPreferences(c);
}

public static KeyStoreController getKeyStore(){
    if( singleton == null){
        singleton = new KeyStoreController(MainApplication.getContext());
    }
    return singleton;
}

public void setPreference(String key, Object value) {
    // The SharedPreferences editor - must use commit() to submit changes
    SharedPreferences.Editor editor = preferences.edit();
    if(value instanceof Integer )
        editor.putInt(key, ((Integer) value).intValue());
    else if (value instanceof String)
        editor.putString(key, (String)value);
    else if (value instanceof Boolean)
        editor.putBoolean(key, (Boolean)value);
    else if (value instanceof Long)
        editor.putLong(key, (Long)value);
    editor.commit();
}

public int getInt(String key, int defaultValue) {
    return preferences.getInt(key, defaultValue);
}

public String getString(String key, String defaultValue) {
    return preferences.getString(key, defaultValue);
}

public boolean getBoolean(String key, boolean defaultValue) {
    return preferences.getBoolean(key, defaultValue);
}

public long getLong(String key, long defaultValue) {
    return preferences.getLong(key, defaultValue);
}

答案 2 :(得分:0)

如果您绕过Context,最好通过ApplicationContext。如果您只是引用静态ApplicationContext,然后在您的类中需要它们时使用SharedPreferences可能会更容易(如果该方法适用于您)。

如果你有一个Context来电的参数,那么你不应该担心泄密,除非你坚持下去。

但是,我认为你在做概念上做得很好。

答案 3 :(得分:0)

为什么不创建一个静态类并将其用作实用程序,因此您根本不必保留对SharedPreferences的引用。您也永远不必初始化此类的实例,只要您有要提供的上下文,就可以调用PreferencesUtil.getUserName(context)。

public static class PreferencesUtil{

    private static final String USER_NAME_KEY = "uname";

    public static void setUserName(String name, Context c){
        SharedPreferences sharedPref = getPreferences(c);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putString(USER_NAME_KEY, name);
        editor.commit();
    }


    public static String getUserName(Context c){
        return getPreferences(c).getString(USER_NAME_KEY, "");
    }

    private SharedPreferences getPreferences(Context context){
        return context.getPreferences(Context.MODE_PRIVATE);
    }

}