Синглтоны против контекста приложения в Android?

Вспоминая эту статью, перечисляющую несколько проблем использования синглтонов и, увидев несколько примеров приложений для Android, использующих одноэлементный шаблон, интересно, стоит ли использовать синглтоны вместо отдельных экземпляров, разделяемых через глобальное состояние приложения (подклассирование android.os.Application и получение его через context.getApplication()).

Какие преимущества/недостатки имеют оба механизма?

Честно говоря, я ожидаю тот же ответ в этом сообщении Шаблон Singleton с веб-приложением, не очень хорошая идея!, но применительно к Android. Я прав? Чем отличается в DalvikVM в противном случае?

EDIT: Мне хотелось бы получить мнения по нескольким аспектам:

  • Синхронизация
  • Повторное использование
  • Тестирование

Ответ 1

Я очень не согласен с ответом Дианны Хакборн. Мы понемногу удаляем все синглтоны из нашего проекта в пользу облегченных объектов с заданными областями, которые могут быть легко восстановлены, когда вы действительно в них нуждаетесь.

Синглтоны - это кошмар для тестирования и, если он лениво инициализирован, будет вводить "неопределенность состояния" с тонкими побочными эффектами (которые могут внезапно появиться при перемещении вызовов getInstance() из одной области в другую). Видимость упоминалась как еще одна проблема, и поскольку одноточие подразумевает "глобальный" (= случайный) доступ к общему состоянию, могут возникать тонкие ошибки, если они не синхронизируются в параллельных приложениях.

Я считаю это анти-шаблоном, это плохой объектно-ориентированный стиль, который по существу сводится к поддержанию глобального состояния.

Чтобы вернуться к вашему вопросу: Хотя контекст приложения можно рассматривать как одноэлементный, он управляется инфраструктурой и имеет четко определенный жизненный цикл, область действия и путь доступа. Поэтому я считаю, что если вам нужно управлять глобально-глобальным состоянием, он должен идти сюда, нигде больше. Для чего-либо еще, переосмыслите, если вам действительно нужен объект singleton, или можно будет также переписать ваш одноэлементный класс, чтобы вместо этого создавать небольшие, недолговечные объекты, которые выполняют задачу под рукой.

Ответ 2

Я очень рекомендую одиночные игры. Если у вас есть синглтон, которому нужен контекст, выполните следующие действия:

MySingleton.getInstance(Context c) {
    //
    // ... needing to create ...
    sInstance = new MySingleton(c.getApplicationContext());
}

Я предпочитаю синглеты для приложения, потому что это помогает поддерживать гораздо более организованное и модульное приложение - вместо того, чтобы иметь одно место, где должно поддерживаться все ваше глобальное состояние в приложении, каждая отдельная часть может позаботиться о себе. Также тот факт, что синглтоны лениво инициализируют (по запросу) вместо того, чтобы вести вас по пути выполнения всей инициализации вверх в Application.onCreate(), хорошо.

Нет ничего по-настоящему неправильного в использовании синглетонов. Просто используйте их правильно, когда это имеет смысл. В базе данных Android на самом деле их много, поскольку они поддерживают кэширование загружаемых ресурсов в каждом процессе и другие подобные вещи.

Кроме того, для простых приложений многопоточность не становится проблемой с синглонами, потому что по дизайну все стандартные обратные вызовы к приложению отправляются по основному потоку процесса, поэтому вы не будете иметь многопоточность, если вы не представите ее явно через потоки или неявно путем публикации поставщика контента или службы IBinder другим процессам.

Просто подумайте о том, что вы делаете.:)

Ответ 3

От: http://developer.android.com/reference/android/app/Application.html

Как правило, нет необходимости в подклассе Application. В большинстве случаев, статические синглеты могут обеспечивать такую ​​же функциональность в более модульной путь. Если ваш singleton нуждается в глобальном контексте (например, для регистрации широковещательные приемники), функция для ее получения может быть предоставлена Контекст, который внутренне использует Context.getApplicationContext(), когда сначала построим синглтон.

Ответ 4

У меня была та же проблема: Singleton или сделать подкласс android.os.Application?

Сначала я попытался с помощью Singleton, но мое приложение в какой-то момент делает звонок в браузер

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));

и проблема в том, что если в телефоне недостаточно памяти, большинство ваших классов (даже Singletons) очищаются, чтобы получить некоторую память, поэтому, возвращаясь из браузера в мое приложение, он разбивается каждый раз.

Решение: поместите необходимые данные в подкласс класса Application.

Ответ 5

Приложение отличается от Singleton. Причины:

  • Метод приложения (например, onCreate) вызывается в потоке ui;
  • метод singleton можно вызывать в любом потоке;
  • В методе "onCreate" приложения вы можете создать экземпляр Handler;
  • Если singleton выполняется в ничейной нити, вы не можете экземпляр обработчика;
  • Приложение имеет возможность управлять жизненным циклом деятельности в app.It имеет метод "registerActivityLifecycleCallbacks". Но в синглтонах нет способность.

Ответ 6

Рассмотрим оба одновременно:

  • с объектами singleton как статические экземпляры внутри классов.
  • с общим классом (Context), который возвращает экземпляры singleton для всех объектов singelton в вашем приложении, что имеет то преимущество, что имена методов в Context будут иметь смысл, например: context.getLoggedinUser() вместо User.getInstance().

Кроме того, я предлагаю вам расширить свой контекст, чтобы включить не только доступ к объектам singleton, но и некоторые функции, к которым необходимо получить доступ во всем мире, например, например: context.logOffUser(), context.readSavedData() и т.д. Вероятно, переименование Тогда контекст к фасаду имел смысл.

Ответ 7

Они на самом деле то же самое. Там одно отличие, которое я вижу. С помощью класса Application вы можете инициализировать свои переменные в Application.onCreate() и уничтожить их в Application.onTerminate(). При использовании singleton вы должны полагаться на инициализацию VM и уничтожение статики.

Ответ 8

Мои 2 цента:

Я заметил, что некоторые одиночные/статические поля были сброшены, когда моя активность была уничтожена. Я заметил это на некоторых устройствах с низким уровнем 2.3.

Мое дело было очень простым: у меня просто есть приватная "init_done" и статический метод "init", который я вызывал из activity.onCreate(). Я замечаю, что метод init был повторно запущен при некотором повторном создании активности.

Пока я не могу подтвердить свое утверждение, это может быть связано с WHEN, когда singleton/class был создан/использован первым. Когда действие уничтожается/перерабатывается, кажется, что весь класс, который относится только к этому действию, также перерабатывается.

Я перенес свой экземпляр singleton в подкласс класса Application. Я присоединяюсь к ним из экземпляра приложения. и, с тех пор, снова не заметил проблему.

Я надеюсь, что это может помочь кому-то.

Ответ 9

Моя активность вызывает финиш() (которая не делает ее законченной немедленно, но в конечном итоге) и вызывает Google Street Viewer. Когда я отлаживаю его на Eclipse, мое соединение с приложением прерывается, когда вызывается Street Viewer, который я понимаю как закрытое (целое) приложение, предположительно, чтобы освободить память (поскольку одно завершение действия не должно вызывать такого поведения), Тем не менее, я могу сохранить состояние в Bundle через onSaveInstanceState() и восстановить его в методе onCreate() следующего действия в стеке. Либо используя статическое одноэлементное или подклассическое приложение, я сталкиваюсь с закрытием и потерей приложения (если я не сохраню его в Bundle). Поэтому по моему опыту они одинаковы в отношении сохранения государства. Я заметил, что соединение потеряно в Android 4.1.2 и 4.2.2, но не на 4.0.7 или 3.2.4, что, по моему мнению, говорит о том, что механизм восстановления памяти в какой-то момент изменился.

Ответ 10

Из пресловутого устья лошади...

При разработке приложения вам может потребоваться обмен данными, контекстом или службами по всему миру. Например, если ваше приложение имеет данные сеанса, такие как пользователь, который в настоящий момент вошел в систему, вы, вероятно, захотите предоставить эту информацию. В Android шаблон для решения этой проблемы состоит в том, чтобы ваш экземпляр android.app.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 static void onCreate(){
        super.onCreate()
        sInstance = this;
    }
}