English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Пример реализации паттерна Singleton в Android с подробным объяснением

В этой статье рассматривается пример реализации паттерна Singleton в Android-программировании. Предлагается для обсуждения и использования, подробности см. ниже:

Первый раздел: представление

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

Второй раздел: определение

Гарантирует, что у определенного класса есть только один экземпляр, и он сам создает и предоставляет этот экземпляр всему системному окружению.

Третий раздел: сцены использования

Гарантирует, что у определенного класса есть только один объект, что предотвращает создание множества объектов, что приводит к избыточному потреблению ресурсов, или что определенный тип объектов должен иметь только один объект. Например, создание объекта требует много ресурсов, таких как доступ к IO и базе данных, в этом случае следует рассмотреть использование паттерна Singleton.

Четвертый раздел: способ реализации

1. Модель 'голодный хан'

Пример кода:

/**
 * Модель 'голодный хан'
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance(){
    if(instance == null){
      instance = new Singleton();
    }
    возврат instance;
  }
}

Преимущества: Задержка загрузки (загружается только при необходимости)

Недостатки: Ненадежна в многопоточных ситуациях, легко может возникнуть несинхронизация, например, при частых读写 операциях с базой данных.

2. Модель 'ленивый хан'

Пример кода:

/**
 * Модель 'ленивый хан'
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static synchronized Singleton getInstance(){
    if(instance == null){
      instance = new Singleton();
    }
    возврат instance;
  }
}

По сравнению с моделью 'голодный хан', метод getInstance() добавляет ключевое слово synchronized, что означает, что getInstance является синхронизированным методом, что гарантирует уникальность единственного объекта в многопоточных ситуациях. Однако, если подумать, можно обнаружить проблему: даже если instance已经被 инициализирован (он инициализируется в первый раз при вызове), каждый вызов метода getInstance выполняется синхронизированно, что потребляет избыточные ресурсы. Это и есть最大的 проблема модели 'голодный хан'.

Преимущества: Решает проблему нестабильности в потоках.

Недостатки: Необходимо своевременно создавать экземпляр при первом загрузке, что может несколько замедлить的反应, самая большая проблема - это избыточная синхронизация при каждом вызове getInstance, что приводит к избыточным затратам на синхронизацию.

Дополнение: В исходном коде Android используются такие методы singleton, как InputMethodManager, AccessibilityManager и т.д., которые используют этот паттерн singleton

3. Double Check Lock (DCL)

Пример кода:

/**
 * Паттерн Singleton с двойной проверкой блокировки (DCL)
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance(){
    if(instance == null){
      synchronized (Singleton.class) {
        if(instance == null) {
          instance = new Singleton();
        }
      }
    }
    возврат instance;
  }
}

Основная идея этой программы заключается в методе getInstance, где можно увидеть, что метод getInstance производит двойную проверку пустоты instance: первый уровень проверки主要用于 предотвращения избыточной синхронизации, а второй уровень проверки предназначен для создания экземпляра в случае null.

Предположим, что поток A выполняет до строки instance = new Singleton(), здесь это выглядит как одно предложение, но на самом деле это не атомарная операция, это предложение в конечном итоге будет скомпилировано в несколько инструкций汇编а, и оноroughly делает три вещи:

(1) Выделение памяти для экземпляра Singleton;

(2) Вызов конструктора Singleton(), инициализация членских полей;

(3) Указать instance объект на выделенное место в памяти (в этот момент instance уже не null).

Но, так как компилятор Java позволяет процессору выполнять инструкции в порядке, отличном от исходного, а также из-за правил модели памяти Java (JMM) до JDK1.5, касающихся порядка записи из кэша и регистров в основную память, порядок выполнения второго и третьего предложений не гарантируется. Иначе говоря, порядок выполнения может быть 1-2-3 или 1-3-2. Если это последнее, и перед выполнением третьей инструкции, но после выполнения второй, поток был переключен на поток B, в этот момент instance уже была выполнена в потоке A, и instance уже не была пустой, поэтому поток B напрямую взял instance, и при последующем использовании возникла ошибка, это и есть проблема с失效ом DCL (двойной проверкой блокировки), и такие трудные для отслеживания и воспроизведения ошибки могут оставаться скрытыми в течение долгого времени.

С JDK 1.5 SUN официально обратил внимание на эту проблему, откорректировал JVM и конкретизировал ключевое слово volatile, поэтому, если JDK является 1.5 или более поздней версии, достаточно изменить определение instance на private volatile static Singleton instance, чтобы гарантировать, что объект instance каждый раз будет читаться из основного ящика памяти, и можно использовать способ DCL для реализации шаблона единственного экземпляра.もちろん、volatile或多或少会影响性能,但考虑到程序的正确性,这种性能的牺牲还是值得的。

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

Недостатки: При первом загрузке может наблюдаться замедление реакции, также это может быть связано с причиной Java Memory Model, и иногда может быть неудача. В условиях высокой параллелизации также существуют некоторые недостатки, хотя вероятность их возникновения很小.

Дополнение: В开源 проекте Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader) используется именно этот способ.
Модель DCL — это один из самых часто используемых способов реализации шаблона единственного экземпляра, она может инстанцировать объект шаблона единственного экземпляра только по мере необходимости и может обеспечить уникальность объекта шаблона единственного экземпляра в подавляющем большинстве сценариев, за исключением того, если ваш код используется в сложных сценариях параллельной обработки или в версиях JDK, более низких, чем 6, в этом случае这种方式, как правило, может удовлетворить потребности.

4、Статическая внутренняя класс шаблон единственного экземпляра

DCL хотя и в определенной степени решает проблемы, такие как расход ресурсов, избыточная синхронизация и безопасность потоков, но в некоторых случаях все же可能出现失效的问题. Эта проблема называется двойной проверкой блокировки (DCL) и обсуждается в конце книги «Java Concurrency in Practice», где указывается, что это «улучшение» является уродливым и не рекомендуется к использованию. Вместо этого рекомендуется использовать следующий код:

Пример кода:

/**
 * Статическая внутренняя класс шаблон единственного экземпляра
 */
public class Singleton {
  private Singleton(){}
  public static Singleton getInstance(){
    return SingletonHolder.instance;
  }
  /**
   * Статическая внутренняя класс
   * Отложенная загрузка, уменьшение расходов на память
   */
  private static class SingletonHolder{
    private static final Singleton instance = new Singleton();
  }
}

Когда первый раз загружается класс Singleton, instance не инициализируется, только при первом вызове метода getInstance Singleton instance будет инициализирована. Таким образом, первый вызов метода getInstance вызовет загрузку класса SingletonHolder виртуальной машиной, этот метод не только обеспечивает безопасность для многоthread, но и гарантирует уникальность单体 объекта, также откладывает инициализацию单体, поэтому это является рекомендуемым способом реализации шаблона单体.

Преимущества: отложенная загрузка, безопасна для многоthread (в java классы загружаются взаимно исключающими), также уменьшает использование памяти

5. Модуль单体

Ранее были рассмотрены некоторые реализации шаблона单体, но эти реализации или несколько сложны, или могут возникнуть проблемы в некоторых случаях.

Пример кода:

/**
 * Модель单体-модуля
 */
public enum Singleton {
  /**
   * 1. С Java 1.5;
   * 2. Бесплатно предоставляет механизм сериализации;
   * 3. Абсолютно предотвращает многократное создание, даже при сложной сериализации или отражательных атаках;
   */
  instance;
  private String others;
  Singleton() {
  }
  public String getOthers() {
    return others;
  }
  public void setOthers(String others) {
    this.others = others;
  }
}

Простота записи - это одно из основных преимуществ单体-модели. Модуль в Java такой же, как и обычный класс, он может иметь поля и свои методы.最重要的是, создание по умолчанию модуля безопасно для многоthread и всегда является单体.

Почему так? В некоторых из вышеупомянутых реализаций单体 шаблона в одном случае они могут создать новый объект, это десериализация.

Сериализация позволяет записать экземпляр单体 в диск, а затем вернуть его, что эффективно позволяет получить экземпляр. Даже если конструктор является частным, при десериализации можно создать новый экземпляр класса через особый путь, что эквивалентно вызову конструктора класса. Операция десериализации предоставляет особую钩овую функцию, в классе есть частный метод, который ин实例ализирован, readResolve(), который позволяет разработчику контролировать десериализацию объекта. Например, если нужно предотвратить создание нового объекта单体 при десериализации, необходимо добавить следующий метод:

private Object readResolve() throws ObjectStreamException {
  возврат instance;
}

Это означает, что в методе readResolve возвращается объект instance, а не создается новый объект по умолчанию. Для энумов这个问题 не существует, так как при десериализации они не создают новых экземпляров.

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

НедостаткиС Java 1.5 поддерживается.

Ниже рассмотрены 5 методов создания Singleton, и вы можете использовать их в своих личных проектах, учитывая их преимущества и недостатки.

Читатели, интересующиеся дополнительной информацией о Android, могут ознакомиться с нашей темой: «Введение в разработку Android и руководства по продвинутым методам», «Советы по отладке и решения проблем Android» и «Обзор основных компонентов Android, методов представления View и布局 layout, а также методов использования контроллеров Android».

Надеюсь, что информация, изложенная в этой статье, поможет вам в разработке Android-приложений.

Заявление: содержимое статьи взято из Интернета, авторские права принадлежат авторам, контент предоставлен пользователями Интернета, сайт не имеет права собственности, не undergone редактирование, не несет ответственности за соответствующие юридические вопросы. Если вы обнаружите подозрительное содержимое о нарушении авторских прав, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма, пожалуйста, замените # на @) для сообщения о нарушении и предоставьте соответствующие доказательства. Если будет установлено, что это нарушение, сайт немедленно удалил涉嫌侵权的内容.

Основной учебник
Рекомендуется для вас