Google Android - это несложно

Добро пожаловать на форум сайта startandroid.ru
Текущее время: 14 дек 2018, 20:13

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 6 ] 
Автор Сообщение
СообщениеДобавлено: 15 май 2013, 13:32 
Аватар пользователя

Зарегистрирован: 09 янв 2012, 14:45
Сообщений: 2386
Откуда: Самара
Благодарил (а): 102 раз.
Поблагодарили: 321 раз.
(Update 12.08.2013. Изменена логика работы с устройствами версии API>=11, всё таки флаг MODE_MULTI_PROCESS понадобился в полной мере).

Заметка касается работы с данными именно в одном приложении. (Если вам надо "расшарить" настройки на другие приложения, то вам наверное подойдет ContentProvider, но тема не про это)
1. Чего бы хотелось.
Думаю многие согласятся, что чем больше приложение, тем больше настроек оно может использовать в разных местах программы. Для этого нам может пригодиться единый класс управления настройками, будь он статический, либо singleton. Отлично, давайте возьмем singleton, будем обновлять настройки, после чего через геттеры и сеттеры использовать значения. Есть одна проблема.
2. Подводные камни.
Dalvik машина запускает каждый процесс (не путать с потоком) в отдельной своей копии, и как следствие мы получаем не один объект синглтона, а два (если пользуемся настройками с двух процессов). Это очень плохо. И это нужно понимать. С потоками всё в порядке. В рамках одного процесса все его потоки используют один экземпляр (один объект синглтона).
Да, но причем тут собственно настройки, если они сохраняются на носитель сразу? Ведь без разницы должно быть, записываем на носитель и читаем с любого процесса тот файл, что с настройками. И да, это действительно работало. Если кому интересно, вот информация о том, когда это работает, а когда нет.
Кто не силён в английском, я поясню всю тяжесть проблемы при работе с двумя процессами.
- До версии 2.3 это прекрасно работало.
- Вресия 2.3 - не работает.
- Версия >2.3 - надо использовать флаг MODE_MULTI_PROCESS
На вопрос "почему?", скажу что Google скорее всего с версии 2.3 начал использовать кеширование в память настроек и не сразу сохранять настройки. А это, как я уже указал, в двух процессах разные объекты. В итоге в одном процессе меняем - в другом читаем старые значения.
Но во всей это истории я не понял гугла. Ну сделали они кеширование с API 9, но почему флаг MODE_MULTI_PROCESS появился только с 11? Они полностью проигнорировали 2.3 линейку, которая на март 2013 составляет 38 процентов от всех пользователей!
3. Как обойти.
А всё гениальное просто. Сделайте commit настройкам после изменения (смотреть второй кусок кода!). Вот тут нашел ответ на данный вопрос. Немного кода:
Код: [ Загрузить ] [ Скрыть ]
Using Java Syntax Highlighting
  1. /* Класс - менеджер для работы с данными настроек */
  2. public class Preferences {
  3.         private static final Context c = App.getInstance().getApplicationContext();
  4.         public static final String NAVI_ALTERNATIVE_ROUTES_KEY       = c.getString(R.string.navi_alternative_routes_key);
  5.         public static final String NAVI_RECALCULATION_ROUTE_KEY      = c.getString(R.string.navi_recalculation_route_key);
  6.         private SharedPreferences mPreferences;
  7.         private boolean useAlternateRoutes;
  8.         private int alternateRouteDistance;
  9.  
  10.         private Preferences() { }
  11.        
  12.         public static synchronized Preferences getInstance() {
  13.                 if (instance==null) {
  14.                         instance = new Preferences();
  15.                 }
  16.                 return instance;
  17.         }
  18.  
  19.         /* Обновляет переменные класса в соответствии с выбранными настройками */
  20.     public synchronized void updatePreferences() {
  21.         if (Build.VERSION.SDK_INT < 11) {
  22.             this.mPreferences = PreferenceManager.getDefaultSharedPreferences(c);
  23.         } else {
  24.             this.mPreferences = c.getSharedPreferences(c.getPackageName() +  "_preferences",Context.MODE_MULTI_PROCESS);
  25.         }
  26.         useAlternateRoutes = mPreferences.getBoolean(NAVI_ALTERNATIVE_ROUTES_KEY, false);
  27.         alternateRouteDistance = Integer.parseInt(mPreferences.getString(NAVI_RECALCULATION_ROUTE_KEY, "100"));
  28.     }
  29.  
  30.         public int getAlternateRouteDistance() {
  31.                 return alternateRouteDistance;
  32.         }
  33.  
  34.         public boolean isUseAlternateRoutes() {
  35.                 return useAlternateRoutes;
  36.         }
  37. }
  38.  

Как-то так, урезанно, выглядит singleton у нас. Думаю тут пояснять нечего. Скажу лишь что названия ключей я беру с ресурсов. Это по желанию. Таким способом просто надо будет в дальнейшем менять только ресурс. Ах да, App класс - это вспомогательный класс, унаследованный от Application, через который мы получаем applicationContext. Вам решать, как получить доступ к ресурсам. Можете, как уже сказал, работать через обычные строки.
А теперь непосредственно к тому, где делать коммит настроек, потому как если это сделать в конце метода updatePreferences, то проблема не уходит.
Код: [ Загрузить ] [ Скрыть ]
Using Java Syntax Highlighting
  1. public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
  2.  
  3.     @Override
  4.     public void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         addPreferencesFromResource(R.xml.preferences); //ссылка на ваши настройки
  7.     }
  8.    
  9.     @Override
  10.     protected void onResume() {
  11.         super.onResume();
  12.         getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
  13.     }
  14.  
  15.     @Override
  16.     protected void onPause() {
  17.         super.onPause();
  18.         getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
  19.     }
  20.  
  21.  
  22.     /**
  23.      * Раcсылает Broadcast, уведомляющий о изменении настроек.
  24.      * Содержит в себе ключ, который изменился
  25.      */
  26.         @Override
  27.         public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
  28.         //тут берем измененный sharedPreference и делаем этим настройкам коммит
  29.                 sharedPreferences.edit().commit();
  30.         //а после бросаем бродкаст с уведомлением о том, что настройки изменились.
  31.                 Intent intent = new Intent("settings have changed");
  32.                 intent.putExtra("key", key);
  33.         sendBroadcast(intent);
  34.         }
  35. }
  36.  

Создаем новую активность, которая наследуется от PreferenceActivity и регистрирует слушатель OnSharedPreferenceChangeListener. В метод onSharedPreferenceChanged будет передаваться тот самый объект настроек, который изменился. Берем его и делаем commit, после чего бросаем Broadcast с уведомлением о том, что настройки изменились. Ну а после, как поймали Broadcast, делаем Preferences.getInstance().updatePreferences();

_________________
Изображение

А тот ли ты путь выбрал, разработчик?
Хочешь знать ошибки ответ? Загляни в logcat!


Последний раз редактировалось Mikhail_dev 12 авг 2013, 22:19, всего редактировалось 3 раз(а).

Вернуться наверх
 Профиль  
 
СообщениеДобавлено: 02 июл 2013, 05:14 
Аватар пользователя

Зарегистрирован: 18 окт 2012, 11:17
Сообщений: 1098
Откуда: г. Красноярск
Благодарил (а): 26 раз.
Поблагодарили: 279 раз.
Хорошая статья.
Единственное в тексте говориться о методе updateSettings() (причем в одном случае "S" заглавная, а в другом прописная), в то время как в классе Preferences метод называется updatePreferences(), и еще объявления instance не вижу.


Вернуться наверх
 Профиль  
 
СообщениеДобавлено: 02 июл 2013, 07:24 
Аватар пользователя

Зарегистрирован: 09 янв 2012, 14:45
Сообщений: 2386
Откуда: Самара
Благодарил (а): 102 раз.
Поблагодарили: 321 раз.
Благодарю за указанные ошибки. Действительно, updatePreferences(), а не updateSettings().

_________________
Изображение

А тот ли ты путь выбрал, разработчик?
Хочешь знать ошибки ответ? Загляни в logcat!


Вернуться наверх
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 22:26 
Аватар пользователя

Зарегистрирован: 09 янв 2012, 14:45
Сообщений: 2386
Откуда: Самара
Благодарил (а): 102 раз.
Поблагодарили: 321 раз.
Статья обновлена. С третьего андроида всё таки необходимо использовать флаг MODE_MULTI_PROCESS, поэтому немного изменен код.

_________________
Изображение

А тот ли ты путь выбрал, разработчик?
Хочешь знать ошибки ответ? Загляни в logcat!


Вернуться наверх
 Профиль  
 
СообщениеДобавлено: 08 дек 2013, 23:57 

Зарегистрирован: 08 дек 2013, 12:43
Сообщений: 9
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Кстати было бы неплохо отметить, что singleton вещь практически не реализуемая в андроиде. Единственный возможный вариант - через Application.
Вот тут хорошо написано.


Вернуться наверх
 Профиль  
 
СообщениеДобавлено: 10 дек 2013, 19:49 
Аватар пользователя

Зарегистрирован: 09 янв 2012, 14:45
Сообщений: 2386
Откуда: Самара
Благодарил (а): 102 раз.
Поблагодарили: 321 раз.
Application тоже не может быть сингтоном в полной мере. У нас в проекте два процесса, что влечет за собой два объекта Application, и да, это действительно проблема...

_________________
Изображение

А тот ли ты путь выбрал, разработчик?
Хочешь знать ошибки ответ? Загляни в logcat!


Вернуться наверх
 Профиль  
 
Показать сообщения за:  Сортировать по:  
Начать новую тему Ответить на тему  [ Сообщений: 6 ] 

Часовой пояс: UTC + 3 часа


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB