Синглтоны против контекста приложения в Android?
Вспоминая этот пост, перечисляя несколько проблем использования синглетонов и видя несколько примеров приложений Android, использующих шаблон синглтонов, мне интересно, будет ли хорошей идеей использовать одиночные экземпляры вместо единичных экземпляров, передаваемых через глобальное состояние приложения (создание подклассов android.os.Application и получение его через context.getApplication()).
Какие преимущества / недостатки будут у обоих механизмов?
Честно говоря, я ожидаю того же ответа в этом посте Шаблон Singleton с веб-приложением, Не очень хорошая идея! но применяется к Android. Я прав? Чем отличается DalvikVM от других?
РЕДАКТИРОВАТЬ: Я хотел бы иметь мнения по нескольким аспектам:
- синхронизация
- Повторное использование
- тестирование
10 ответов
Я очень не согласен с ответом Дайан Хакборн. Мы постепенно удаляем все синглтоны из нашего проекта в пользу легких объектов с областью задач, которые могут быть легко воссозданы, когда они вам действительно понадобятся.
Синглтоны - это кошмар для тестирования и, если их лениво инициализировать, они привнесут "неопределенность состояния" с тонкими побочными эффектами (которые могут внезапно появиться при перемещении вызовов на getInstance()
из одной области в другую). Видимость упоминается как еще одна проблема, и поскольку синглтоны подразумевают "глобальный" (= случайный) доступ к общему состоянию, могут возникать тонкие ошибки, если они не синхронизированы должным образом в параллельных приложениях.
Я считаю это анти-паттерном, плохим объектно-ориентированным стилем, который по сути сводится к поддержанию глобального состояния.
Чтобы вернуться к вашему вопросу:
Хотя контекст приложения можно считать одноэлементным, он управляется инфраструктурой и имеет четко определенные жизненный цикл, область действия и путь доступа. Поэтому я считаю, что если вам нужно управлять глобальным состоянием приложения, оно должно идти сюда, больше никуда. Для чего-то еще: подумайте, действительно ли вам нужен одноэлементный объект, или если бы можно было переписать ваш одноэлементный класс, чтобы вместо этого создать экземпляры небольших, недолговечных объектов, которые выполняют задачу под рукой.
Я очень рекомендую синглтоны. Если у вас есть синглтон, который нуждается в контексте, есть:
MySingleton.getInstance(Context c) {
//
// ... needing to create ...
sInstance = new MySingleton(c.getApplicationContext());
}
Я предпочитаю одиночные, а не прикладные приложения, потому что это помогает сделать приложение намного более организованным и модульным - вместо того, чтобы иметь одно место, где необходимо поддерживать все ваше глобальное состояние в приложении, каждый отдельный элемент может позаботиться о себе сам. Также хорошо то, что синглтоны лениво инициализируются (по запросу) вместо того, чтобы вести вас по пути полной инициализации в Application.onCreate().
В использовании синглетонов нет ничего плохого. Просто используйте их правильно, когда это имеет смысл. Фреймворк Android на самом деле имеет много из них, чтобы поддерживать кеширование загруженных ресурсов для каждого процесса и другие подобные вещи.
Также для простых приложений многопоточность не становится проблемой для синглетонов, потому что все стандартные обратные вызовы приложения отправляются в основной поток процесса, поэтому многопоточность не будет возникать, если вы не представите ее явно через потоки или неявно путем публикации поставщика контента или службы IBinder для других процессов.
Просто задумайтесь о том, что вы делаете.:)
От: Разработчик> Ссылка - Приложение
Обычно нет необходимости создавать подкласс Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), функции для его получения может быть задан контекст, который внутренне использует Context.getApplicationContext() при первом создании синглтона.
У меня была такая же проблема: синглтон или сделать подкласс android.os.Application?
Сначала я попробовал с Singleton, но мое приложение в какой-то момент звонит в браузер
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
и проблема в том, что, если у телефона недостаточно памяти, большинство ваших классов (даже синглтонов) очищаются, чтобы получить немного памяти, поэтому при возврате из браузера в мое приложение каждый раз происходит сбой.
Решение: поместить необходимые данные в подкласс класса Application.
Приложение не совпадает с Singleton. Причины:
- Метод приложения (например, onCreate) вызывается в потоке пользовательского интерфейса;
- метод синглтона может быть вызван в любом потоке;
- В методе "onCreate" приложения вы можете создать экземпляр Handler;
- Если синглтон выполняется в потоке без пользовательского интерфейса, вы не можете создать экземпляр Handler;
- Приложение имеет возможность управлять жизненным циклом действий в приложении. У него есть метод "registerActivityLifecycleCallbacks". Но у синглетонов нет такой возможности.
Рассмотрим оба одновременно:
- наличие одноэлементных объектов в качестве статических экземпляров внутри классов.
- наличие общего класса (Context), который возвращает единичные экземпляры для всех объектов singelton в вашем приложении, что имеет преимущество в том, что имена методов в Context будут иметь смысл, например: context.getLoggedinUser() вместо User.getInstance().
Кроме того, я предлагаю вам расширить свой Контекст, включив в него не только доступ к одноэлементным объектам, но и некоторые функции, к которым необходимо обращаться глобально, например: context.logOffUser(), context.readSavedData() и т. Д. Вероятно, переименуйте Контекст в Фасад будет иметь смысл тогда.
Они на самом деле одинаковы. Есть одно отличие, которое я вижу. С помощью класса Application вы можете инициализировать свои переменные в Application.onCreate() и уничтожить их в Application.onTerminate(). С синглтоном вам нужно полагаться на инициализацию и уничтожение статических ВМ.
Из уст пословиц лошади
При разработке приложения может возникнуть необходимость в общем доступе к данным, контексту или службам по всему приложению. Например, если в вашем приложении есть данные сеанса, такие как зарегистрированный в данный момент пользователь, вы, вероятно, захотите предоставить эту информацию. В Android шаблон решения этой проблемы заключается в том, чтобы ваш экземпляр android.app.Application владел всеми глобальными данными, а затем обрабатывал ваш экземпляр Application как одиночный объект со статическими средствами доступа к различным данным и службам.
При написании приложения для Android вы гарантированно имеете только один экземпляр класса android.app.Application, и поэтому безопасно (и рекомендуется командой Google Android) рассматривать его как одиночный. То есть вы можете безопасно добавить статический метод getInstance() в вашу реализацию приложения. Вот так:
public class AndroidApplication extends Application {
private static AndroidApplication sInstance;
public static AndroidApplication getInstance(){
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
}
Мои 2 цента:
Я заметил, что некоторые синглтон / статические поля были сброшены, когда моя деятельность была разрушена. Я заметил это на некоторых устройствах с низким уровнем 2.3.
Мой случай был очень прост: у меня просто есть закрытое поле "init_done" и статический метод "init", который я вызвал из activity.onCreate(). Я замечаю, что метод init повторно выполнял себя при некотором воссоздании действия.
Хотя я не могу доказать свое утверждение, оно может быть связано с тем, КОГДА синглтон / класс был создан / использован первым. Когда действие уничтожается / рециркулируется, кажется, что все классы, на которые ссылается только это действие, тоже перерабатываются.
Я переместил свой экземпляр singleton в подкласс Application. Я получаю доступ к ним из экземпляра приложения. и, с тех пор, не заметил проблемы снова.
Я надеюсь, что это может кому-то помочь.
Моя активность вызывает метод finish() (который не приводит к немедленному завершению, но в конечном итоге будет выполнен) и вызывает Google Street Viewer. Когда я отлаживаю его в Eclipse, мое соединение с приложением разрывается, когда вызывается Street Viewer, что я понимаю как закрытое (целое) приложение, предположительно для освобождения памяти (так как одиночное завершающееся действие не должно вызывать такое поведение), Тем не менее, я могу сохранить состояние в Bundle с помощью onSaveInstanceState() и восстановить его в методе onCreate() следующего действия в стеке. При использовании статического одноэлементного приложения или приложения подклассов я сталкиваюсь с состоянием закрытия и потери приложения (если я не сохраню его в Bundle). Так что из моего опыта они одинаковы в отношении сохранения государства. Я заметил, что соединение потеряно в Android 4.1.2 и 4.2.2, но не в 4.0.7 или 3.2.4, что в моем понимании предполагает, что механизм восстановления памяти изменился в какой-то момент.