Intereting Posts
SDK Местоположение не найдено Android Studio + Gradle Android: 2D. OpenGl или android.graphics? Как найти слово в строке и выделить слово в текстовом представлении в android? Является ли андроид: exported = "true" действительно необходимым для службы проверки подлинности? Как настроить IntelliJ для Mac, чтобы показать документацию на Java и Android в редакторе (офлайн, а не онлайн)? В приложении Billing v3 IllegalArgumentException с использованием IabHelper Не найден ресурс, который соответствует имени '@ style / Theme.Holo.Light.DarkActionBar' Ошибка во время синхронизации: тайм-аут при развертывании apk на устройстве с использованием maven Ошибка приложения на устройстве HTC M8 с ОС Lollipop для вызова веб-службы Не удается установить APK на моем собственном сервере Apache Как получить доступную кнопку? Как обращаться с уведомлением о появлении на Android, если он отображается автоматически GCM Фоновая привязка: исправлено не работает на android / mobile Android: как подключить два вида с коннектором (криволинейная линия) динамически? Не удалось реализовать дескрипторы в android

MODE_MULTI_PROCESS для SharedPreferences не работает

У меня есть SyncAdapter работающий на собственном процессе отдельно от основного процесса приложения.

Я использую статический класс-оболочку вокруг своих SharedPreferences который создает статический объект при загрузке процесса (Application onCreate ) следующим образом:

 myPrefs = context.getSharedPreferences(MY_FILE_NAME, Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE); 

Обертка имеет методы get и set, например:

 public static String getSomeString() { return myPrefs.getString(SOME_KEY, null); } public static void setSomeString(String str) { myPrefs.edit().putString(SOME_KEY, str).commit(); } 

Как SyncAdapter и приложение используют этот класс-оболочку для редактирования и получения из prefs, это работает иногда, но много раз я вижу, что SyncAdapter получает старые / отсутствующие префы при обращении к префайлам, в то время как основное приложение видит последние изменения должным образом.

Согласно документам, я думаю, что флаг MODE_MULTI_PROCESS должен работать так, как я ожидаю, чтобы оба процесса могли видеть последние изменения, но это не работает.

Обновить:

По предложению x90 я пробовал воздержаться от использования статического объекта SharedPreferences и вместо этого вызывал getSharedPreferences для каждого метода get / set. Это вызвало новую проблему, когда файл prefs удаляется (!!!) при одновременном доступе к нескольким процессам. Т.е. я вижу в логарифме:

 (process 1): getName => "Name" (process 2): getName => null (process 1): getName => null 

И с этого момента все префы, сохраненные на объекте SharedPreferences были удалены.

Вероятно, это результат другого предупреждения, которое я вижу в журнале:

 W/FileUtils(21552): Failed to chmod(/data/data/com.my_company/shared_prefs/prefs_filename.xml): libcore.io.ErrnoException: chmod failed: ENOENT (No such file or directory) 

PS Это не детерминированная проблема, я видел вышеупомянутые журналы после аварии, но не смог воссоздать еще на одном устройстве, и до сих пор это не похоже на другие устройства.

ДРУГОЕ ОБНОВЛЕНИЕ:

Я опубликовал отчет об ошибке, написав небольшой метод тестирования, чтобы подтвердить, что это действительно проблема с Android, и наберите https://code.google.com/p/android/issues/detail?id=66625

Solutions Collecting From Web of "MODE_MULTI_PROCESS для SharedPreferences не работает"

Имела точно такую ​​же проблему, и моим решением было написать замену на основе ContentProvider для SharedPreferences. Он работает на 100% многопроцессе.

Я сделал это библиотекой для всех нас. Вот результат: https://github.com/grandcentrix/tray

Я очень быстро посмотрел на код Google и, по-видимому, Context.MODE_MULTI_PROCESS не является реальным способом обеспечения безопасности операций SharedPreferences.

Собственные файлы SharedPreferences не являются безопасными для процесса. (Вероятно, поэтому в документации по SharedPreferences говорится: «В настоящее время этот класс не поддерживает использование нескольких процессов, это будет добавлено позже».)

MODE_MULTI_PROCESS работает только совместно с каждым Context.getSharedPreferences(String name, int mode) : когда вы извлекаете экземпляр SharedPreferences, определяющий флаг MODE_MULTI_PROCESS андроид перезагружает файл настроек, чтобы быть в курсе любой (возможной) параллельной модификации, которая произошла К нему. Если вы сохраните этот экземпляр как член класса (статический или нет), файл настроек не будет перезагружен снова.

Использование Context.getSharedPreferences(...) каждый раз, когда вы хотите писать или читать в настройках, также не является безопасным процессом, но я думаю, что это, вероятно, самое близкое, что вы можете получить к нему в данный момент.

Если вам действительно не нужно читать одно и то же предпочтение от разных процессов, тогда обходным путем может быть использование разных файлов предпочтений для разных процессов.

Я просто столкнулся с той же проблемой. Я переключил свое приложение, чтобы запустить службу в отдельном процессе, и реализованы sharedPreferences.

Две вещи:

1) Используете ли вы Editor.apply() или .commit() ? Я использовал .apply() . Я начал проверять свой файл предпочтений либо после того, как активность, либо служба внесла в него изменения, и когда всякий раз, когда кто-то внесет какие-либо изменения, будет создан новый файл с только измененным значением. IE, значение, записанное в результате действия, будет стерто, когда новое значение было записано / изменено из службы и наоборот. Я переключился на .commit() повсюду, и это уже не так! Из документации: «Обратите внимание, что, когда два редактора изменяют предпочтения в одно и то же время, последний для вызова применяет выигрыши.

2) SharedPreferencesListener не работает в разных процессах даже после перехода на .commit() . Вам нужно будет использовать Messenger Handlers или Broadcast Intents для уведомления об изменении. Когда вы смотрите на документацию для класса SharedPreferences, он даже говорит «Примечание: в настоящее время этот класс не поддерживает использование нескольких процессов. Это будет добавлено позже». http://developer.android.com/reference/android/content/SharedPreferences.html

В этом отношении нам повезло, что мы даже имеем флаг MODE_MULTI_PROCESS работает для чтения / записи из тех же SharedPreferences для разных процессов.

MODE_MULTI_PROCESS для SharedPreferences теперь обесценивается (уровень 23 Android-android-android). Это не было безопасным процессом.

MODE_MULTI_PROCESS устарел на уровне API 23. Вы можете решить эту проблему с помощью ContentProvider. DPreference использует sharepreference обтекания ContentProvider. Он имеет лучшую производительность, чем при использовании sqlite. https://github.com/DozenWang/DPreference

Поскольку MODE_MULTI_PROCESS в настоящее время не поддерживается, я не нашел возможности работать с Shared Preferences между процессами, отличными от работы вокруг него.

Я знаю, что люди делят библиотеки, которые они пишут, чтобы решить эту проблему, но я фактически использовал стороннюю библиотеку, которую я нашел в другом потоке, который реализует SQLLite вместо общих настроек:

https://github.com/hamsterready/dbpreferences

Тем не менее, для меня важно, чтобы я не нашел решения в других решениях, поддерживая автоматическое создание пользовательского интерфейса, уже встроенное в Preference Fragment, – лучше иметь возможность указывать ваши элементы в XML и вызывать addPreferencesFromResource (R.xml.preferences), чем Должны создавать свой пользовательский интерфейс с нуля.

Итак, чтобы выполнить эту работу, я подклассифицировал каждый из элементов Preference, которые мне нужны (в моем случае просто Preference, SwitchPreference и EditTextPreference), и переопределил несколько методов из базовых классов, чтобы включить сохранение в экземпляр DatabaseSharedPreferences, взятый из вышеперечисленного библиотека.

Например, ниже подкласса EditTextPreference и получить ключ предпочтения из базового класса. Затем я переопределяю методы persist и getPersisted в базовом классе Preference. Затем я переопределяю onSetInitialValue, setText и getText в базовом классе EditText.

 public class EditTextDBPreference extends EditTextPreference { private DatabaseBasedSharedPreferences mDBPrefs; private String mKey; private String mText; public EditTextDBPreference(Context context) { super(context); init(context); } public EditTextDBPreference(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public EditTextDBPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public EditTextDBPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } private void init(Context context) { mDBPrefs = new DatabaseBasedSharedPreferences(context); mKey = super.getKey(); } public DatabaseBasedSharedPreferences getSharedDBPreferences() { if (mDBPrefs == null) { return null; } return mDBPrefs; } @Override protected boolean persistBoolean(boolean value) { if (mKey != null) mDBPrefs.putBoolean(mKey,value); return super.persistBoolean(value); } @Override protected boolean persistFloat(float value) { if (mKey != null) mDBPrefs.putFloat(mKey, value); return super.persistFloat(value); } @Override protected boolean persistInt(int value) { if (mKey != null) mDBPrefs.putInt(mKey, value); return super.persistInt(value); } @Override protected boolean persistLong(long value) { if (mKey != null) mDBPrefs.putLong(mKey, value); return super.persistLong(value); } @Override protected boolean persistString(String value) { if (mKey != null) mDBPrefs.putString(mKey, value); return super.persistString(value); } @Override protected boolean getPersistedBoolean(boolean defaultReturnValue) { if (mKey == null) return false; return mDBPrefs.getBoolean(mKey, defaultReturnValue); } @Override protected float getPersistedFloat(float defaultReturnValue) { if (mKey == null) return -1f; return mDBPrefs.getFloat(mKey, defaultReturnValue); } @Override protected int getPersistedInt(int defaultReturnValue) { if (mKey == null) return -1; return mDBPrefs.getInt(mKey, defaultReturnValue); } @Override protected long getPersistedLong(long defaultReturnValue) { if (mKey == null) return (long)-1.0; return mDBPrefs.getLong(mKey, defaultReturnValue); } @Override protected String getPersistedString(String defaultReturnValue) { if (mKey == null) return null; return mDBPrefs.getString(mKey, defaultReturnValue); } @Override public void setKey(String key) { super.setKey(key); mKey = key; } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { setText(restoreValue ? getPersistedString(mText) : (String) defaultValue); } @Override public void setText(String text) { final boolean wasBlocking = shouldDisableDependents(); boolean textChanged = false; if (mText != null && !mText.equals(text)) textChanged = true; mText = text; persistString(text); if (textChanged) { // NOTE: This is a an external class in my app that I use to send a broadcast to other processes that preference settings have changed BASettingsActivity.SendSettingsUpdate(getContext()); } final boolean isBlocking = shouldDisableDependents(); if (isBlocking != wasBlocking) { notifyDependencyChange(isBlocking); } } @Override public String getText() { return mText; } 

Затем вы просто указываете новый элемент в файле preferences.xml и voila! Теперь вы получаете возможность взаимодействия процессов SQLLite и автоматического генерации пользовательского интерфейса PreferenceFragment!

 <com.sampleproject.EditTextDBPreference android:key="@string/pref_key_build_number" android:title="@string/build_number" android:enabled="false" android:selectable="false" android:persistent="false" android:shouldDisableView="false"/>