Безопасно ли хранить статическую ссылку на SharedPreferences и его редактор?
Я собираюсь сделать что-то вроде:
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 ответов
чтобы ответить на вопрос авторитетно, это безопасное для хранения SharedPreferences
экземпляр как статическая ссылка. Согласно javadocs это синглтон, поэтому его источник из getSharedPreferences
уже является статической ссылкой.
это не безопасно для хранения SharedPreferences.Editor
потому что возможно, что два потока могут одновременно управлять одним и тем же объектом редактора. Конечно, ущерб, который это вызовет, относительно невелик, если у вас уже есть делал это. Вместо этого получите экземпляр редактора в каждом методе редактирования.
я рекомендую использование статической ссылки на ваш Application
объект вместо передачи Context
объекты для каждого get. Все экземпляры Application
класс-это синглеты для каждого процесса в любом случае и передача Context
объекты обычно плохая практика, потому что она имеет тенденцию приводить к утечкам памяти через удержание ссылок и излишне многословна.
наконец, ответьте на незаданный вопрос, если вы должны лениво-загрузить или жадно-инициализировать ссылку на ваш static SharedPreferences
, вы должны лениво загружать статический метод геттера. Это мая работа жадно-инициализировать ссылку с final static SharedPreferences sReference = YourApplication.getInstance().getSharedPreferences()
в зависимости от цепочки импорта классов, но загрузчику классов было бы слишком легко инициализировать ссылку перед Application
уже onCreate
(где вы инициализируете YourApplication
reference), вызывая исключение нулевого указателя. В заключение:
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
устанавливается дважды, одна и та же базовая ссылка на есть. Относительно статического экземпляра YourApplication.sInstance
, это также безопасно без синхронизации или volatile
ключевое слово. В вашем приложении нет пользовательских потоков, запущенных до YourApplication.onCreate
, и поэтому отношение "происходит до", определенное для вновь созданных потоков, гарантирует, что статическая ссылка будет видна всем будущим потокам, которые могут получить доступ к указанной ссылке.
Я думаю, что это безопасно. Я всегда использую "KeyStoreController" со статической ссылкой на объект SharedPreferences (singleton). Я бы предложил вам использовать контекст приложения вместо передачи контекста каждый раз. Это пример моего кода:
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);
}
если вы проходите вокруг Context
лучше всего пройти по ApplicationContext
. Было бы проще, если бы вы просто сделали static ApplicationContext
для ссылки, а затем просто используйте SharedPreferences
когда они вам нужны из ваших классов (если этот подход работает для вас).
если у вас есть аргумент для вызовов a Context
тогда вам не придется беспокоиться об утечках, если вы держите на него.
но, я думаю, что вы будете просто прекрасно делать то, что вы делаете концептуально.
почему бы просто не создать статический класс и использовать его в качестве утилиты, чтобы вам никогда не приходилось ссылаться на ваш 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);
}
}