Я написал этот ответ еще в '09, когда Android был относительно новым, и в разработке Android было много не очень хорошо проработанных областей. В конце этого поста я добавил длинное приложение, посвященное некоторой критике и подробному изложению философских разногласий, которые у меня возникают с использованием Singletons, а не подклассом Application. Прочитайте это на свой страх и риск.
ОРИГИНАЛЬНЫЙ ОТВЕТ:
Более общая проблема, с которой вы сталкиваетесь, заключается в том, как сохранить состояние нескольких операций и всех частей вашего приложения. Статическая переменная (например, синглтон) является распространенным способом достижения Java. Однако я обнаружил, что более элегантный способ в Android - связать ваше состояние с контекстом приложения.
Как вы знаете, каждое действие также является контекстом, который представляет собой информацию о его среде выполнения в самом широком смысле. Ваше приложение также имеет контекст, и Android гарантирует, что оно будет существовать как единое целое в вашем приложении.
Способ сделать это - создать собственный подкласс android.app.Application , а затем указать этот класс в теге приложения в манифесте. Теперь Android автоматически создаст экземпляр этого класса и сделает его доступным для всего вашего приложения. Вы можете получить к нему доступ из любого context
, используя метод Context.getApplicationContext()
(Activity
также предоставляет метод getApplication()
, который имеет точно такой же эффект). Ниже приведен чрезвычайно упрощенный пример с предостережениями:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
По сути, это тот же эффект, что и при использовании статической переменной или синглтона, но довольно хорошо интегрируется в существующую платформу Android. Обратите внимание, что это не будет работать между процессами (если ваше приложение является одним из редких, в котором есть несколько процессов).
На что обратить внимание из приведенного выше примера; Предположим, что вместо этого мы сделали что-то вроде:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
Теперь эта медленная инициализация (например, попадание на диск, попадание в сеть, все, что блокируется и т. Д.) Будет выполняться каждый раз, когда создается экземпляр приложения! Вы можете подумать, ну, это только один раз для процесса, и я все равно должен буду оплатить стоимость, верно? Например, как упоминает Дайан Хэкборн ниже, вполне возможно, что ваш процесс будет инстанцирован - просто - обработать событие фоновой трансляции. Если ваша обработка широковещания не нуждается в этом состоянии, вы, возможно, просто выполнили целый ряд сложных и медленных операций даром. Ленивый экземпляр - это название игры здесь. Ниже приведен чуть более сложный способ использования приложения, который имеет больше смысла для всего, кроме простейшего использования:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
В то время как я предпочитаю использование подклассов приложений вместо синглетов в качестве более элегантного решения, я бы предпочел, чтобы разработчики использовали синглтоны, если это действительно необходимо, вместо того, чтобы вообще не думать о производительности и многопоточности, связанных с ассоциированием состояния с подклассом приложения.
ПРИМЕЧАНИЕ 1.: Также, как прокомментировал антивирус, для правильной привязки переопределения вашего приложения к вашему приложению необходим тег в файле манифеста. Опять же, смотрите документацию по Android для получения дополнительной информации. Пример:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
ПРИМЕЧАНИЕ 2. user608578 спрашивает ниже, как это работает с управлением жизненными циклами собственных объектов. Я не в курсе использования нативного кода с Android в малейшей степени, и я не квалифицирован, чтобы ответить, как это будет взаимодействовать с моим решением. Если у кого-то есть ответ на этот вопрос, я хочу отдать ему должное и разместить информацию в этом сообщении для максимальной наглядности.
ДОПОЛНЕНИЕ:
Как отмечали некоторые люди, это , а не решение для стойкого состояния, что, возможно, мне следовало бы подчеркнуть больше в первоначальном ответе. То есть это не означает, что это решение для сохранения пользовательской или другой информации, которая должна сохраняться в течение всего времени жизни приложения. Таким образом, я считаю, что большинство нижеприведенных критических замечаний касаются уничтожения приложений в любое время и т. Д., Спорных, поскольку все, что когда-либо требовалось сохранять на диск, не должно храниться через подкласс приложения. Он предназначен для хранения временного, легко восстанавливаемого состояния приложения (например, вошел ли пользователь в систему) и компонентов, которые являются единым экземпляром (например, диспетчер сети приложения) ( NOT singleton! ) в природе.
Дайерман был достаточно любезен, чтобы указать на интересную беседу с Рето Мейером и Дайан Хэкборн , в которой использование подклассов Application не рекомендуется в пользу шаблонов Singleton. Соматик также указал на что-то подобное ранее, хотя я не видел этого в то время. Из-за роли Рето и Дайанны в поддержке платформы Android я не могу добросовестно рекомендовать игнорировать их советы. То, что они говорят, идет. Я хочу не согласиться с мнениями, высказанными в отношении предпочтения синглтонам над подклассами приложений. В своем несогласии я буду использовать концепции, лучше всего объясненные в этом объяснении StackExchange шаблона проектирования Singleton , поэтому мне не нужно определять термины в этом ответе. Я настоятельно рекомендую просмотреть ссылку, прежде чем продолжить. Точка за точкой:
Дайанн заявляет: «Нет причин выделять подклассы из Application. Это ничем не отличается от создания синглтона ...». Это первое утверждение неверно. Для этого есть две основные причины. 1) класс Application обеспечивает лучшую пожизненную гарантию для разработчика приложения; он гарантированно имеет срок действия приложения. Синглтон не привязан ТОЛЬКО к времени жизни приложения (хотя это эффективно). Это может быть не проблема для вашего среднего разработчика приложений, но я бы сказал, что это именно тот тип контракта, который должен предлагать Android API, и он также обеспечивает гораздо большую гибкость для системы Android, сводя к минимуму время жизни связанных данные. 2) Класс Application предоставляет разработчику приложения один держатель экземпляра для состояния, который сильно отличается от держателя состояния Singleton. Список различий см. Выше по ссылке «Синглтон».
Дайанн продолжает: «... скорее всего, вы будете сожалеть об этом в будущем, поскольку ваш объект Application станет таким большим запутанным беспорядком, который должен быть независимой логикой приложения». Это, конечно, не так, но это не причина для выбора Singleton вместо Application подкласса. Ни один из аргументов Дианы не дает оснований полагать, что использование Singleton лучше, чем подкласс Application, все, что она пытается установить, это то, что использование Singleton не хуже, чем подкласс Application, что я считаю ложным.
Она продолжает: «И это более естественно приводит к тому, как вы должны управлять этими вещами - инициализировать их по требованию». Это игнорирует тот факт, что нет никакой причины, по которой вы не можете инициализировать по требованию, также используя подкласс Application. Опять нет разницы.
Дайанн заканчивает словами: «Фреймворк имеет тонны и тонны синглетонов для всех небольших общих данных, которые он поддерживает для приложения, таких как кэши загруженных ресурсов, пулы объектов и т. Д. Он прекрасно работает». Я не утверждаю, что использование Singletons не может работать нормально или не является законной альтернативой. Я утверждаю, что Singletons не обеспечивают столь же сильный контракт с системой Android, как использование подкласса Application, и, кроме того, использование Singletons обычно указывает на негибкий дизайн, который нелегко изменить, и который ведет ко многим проблемам в будущем. ИМХО, надежный контракт, который Android API предлагает разработчикам приложений, является одним из наиболее привлекательных и приятных аспектов программирования на Android и помог привести к скорейшему принятию разработчика, что привело к успеху платформы Android на сегодняшний день. Предложение использовать Singletons неявно отходит от сильного контракта API и, на мой взгляд, ослабляет платформу Android.
Дайанна также прокомментировала ниже, упомянув о дополнительном недостатке использования подклассов приложения, они могут стимулировать или облегчать написание кода с меньшей производительностью. Это очень верно, и я отредактировал этот ответ, чтобы подчеркнуть важность рассмотрения здесь и правильного подхода, если вы используете подклассы Application. Как утверждает Дайанна, важно помнить, что ваш класс Application будет создаваться каждый раз при загрузке вашего процесса (может быть несколько раз за раз, если ваше приложение выполняется в нескольких процессах!), Даже если процесс загружается только для фоновой трансляции событие. Поэтому важно использовать класс Application больше как хранилище для указателей на общие компоненты вашего приложения, а не как место для какой-либо обработки!
Я оставляю вам следующий список недостатков для Singletons, украденный по более ранней ссылке StackExchange:
- Невозможность использования абстрактных или интерфейсных классов;
- Невозможность подкласса;
- Высокая связь по приложению (трудно изменить);
- Трудно проверить (не может подделать / подделать в юнит-тестах);
- Трудно распараллелить в случае изменяемого состояния (требуется расширенная блокировка);
и добавить свой собственный:
- Неясный и неуправляемый пожизненный контракт, не подходящий для разработки Android (или большинства других);