Ответ 1
Я написал этот ответ еще в '09, когда Android был относительно новым, и в разработке Android было много не очень хорошо зарекомендовавших себя областей. Я добавил длинное добавление в нижней части этой публикации, обращаясь к некоторой критике и подробно изложив философское несогласие, которое у меня есть с использованием синглтонов, а не подклассическое приложение. Прочтите его на свой страх и риск.
ОРИГИНАЛЬНЫЙ ОТВЕТ:
Более общая проблема, с которой вы сталкиваетесь, заключается в том, как сохранить состояние в нескольких действиях и во всех частях приложения. Статическая переменная (например, singleton) является обычным способом реализации 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();
...
}
}
Это имеет тот же эффект, что и статическая переменная или singleton, но довольно хорошо интегрируется в существующую платформу 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();
...
}
}
В то время как я предпочитаю использование подклассов приложений для использования синглтонов здесь как более элегантное решение, я бы предпочел, чтобы разработчики использовали синглтоны, если это действительно необходимо, не думая вообще о производительности и многопоточных последствиях связывания состояния с подклассом Application.
ПРИМЕЧАНИЕ 1: Также, как указано в антифашировании, для правильной привязки вашего приложения к вашему приложению в файле манифеста необходим тег. Опять же, см. Документы Android для получения дополнительной информации. Пример:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
ПРИМЕЧАНИЕ 2: user608578 спрашивает ниже, как это работает с управлением жизненными циклами собственных объектов. Я не в курсе, как использовать собственный код с Android в малейшей степени, и я не могу ответить, как это будет взаимодействовать с моим решением. Если у кого-то есть ответ на этот вопрос, я готов отблагодарить их и поместить информацию в этот пост для максимальной видимости.
ДОПОЛНЕНИЕ:
Как отметили некоторые люди, это не решение для состояния постоянного, что, возможно, должно было бы больше подчеркнуть в исходном ответе. То есть это не предназначено для решения проблемы сохранения пользовательской или другой информации, которая должна сохраняться в течение жизни приложений. Таким образом, я считаю, что большая часть критики ниже связана с тем, что Приложения были убиты в любое время и т.д., Но они не были сохранены на подклассе приложения, поскольку все, что нужно было сохранить на диске, не должно храниться на диске. Он предназначен для хранения временного, легко восстанавливаемого состояния приложения (например, если пользователь зарегистрирован, например) и компонентов, которые представляют собой один экземпляр (например, сетевой диспетчер приложений) ( НЕ singleton!) в природе.
Dayerman был достаточно любезен, чтобы указать на интересную беседу с Рето Мейером и Дианой Хакборн, в которых использование подклассов приложений не рекомендуется в пользу Синглтон. Соматик также отметил нечто подобное ранее, хотя я не видел его в то время. Из-за ролей Reto и Dianne в поддержании платформы Android я не могу добросовестно рекомендовать игнорировать их советы. Что они говорят, идет. Я хочу не согласиться с мнением, выраженным в отношении предпочтения Singleton над подклассами приложений. В моем несогласии я буду использовать понятия, которые лучше всего объясняются в этом объяснении StackExchange шаблона проектирования Singleton, так что мне не нужно определять термины в этом ответе. Я настоятельно рекомендую скрыть ссылку, прежде чем продолжить. Точка за точкой:
Dianne утверждает: "Нет оснований для подкласса из приложения. Это ничем не отличается от создания одноэлементного..." Это первое утверждение неверно. Для этого есть две основные причины. 1) Класс Application обеспечивает лучшую пожизненную гарантию для разработчика приложений; гарантируется срок службы приложения. Синглтон не EXPLICITLY привязан к времени жизни приложения (хотя это эффективно). Это может быть не проблема для вашего среднего разработчика приложений, но я бы сказал, что это именно тот тип контракта, который должен предлагать Android API, и он обеспечивает гораздо большую гибкость для системы Android, минимизируя время жизни связанных данные. 2) Класс Application предоставляет разработчику приложения один держатель экземпляра для состояния, который сильно отличается от держателя статуса Singleton. Список различий см. В пояснительной ссылке Singleton выше.
Dianne продолжает: "... скорее всего, вы будете сожалеть об этом в будущем, так как вы обнаружите, что ваш объект приложения становится таким большим запутанным беспорядком того, что должно быть независимой логикой приложения". Это, безусловно, неверно, но это не является основанием для выбора Singleton над подклассом Application. Ни один из аргументов Diane не дает основания полагать, что использование Singleton лучше, чем подкласс приложения, все, что она пытается установить, заключается в том, что использование Singleton не хуже подкласса Application, который, как я считаю, является ложным.
Она продолжает: "И это более естественно ведет к тому, как вы должны управлять этими вещами - инициализируя их по требованию". Это игнорирует тот факт, что нет причин, по которым вы не можете инициализировать по требованию, используя подкласс Application. Опять же нет разницы.
Dianne заканчивается на "В самой структуре есть тонны и тонны синглтонов для всех небольших общих данных, которые он поддерживает для приложения, таких как кеши загруженных ресурсов, пулы объектов и т.д. Он отлично работает". Я не утверждаю, что использование Singletons не может работать нормально или не является законной альтернативой. Я утверждаю, что Singletons не обеспечивают столь сильный контракт с системой Android, как использование подкласса Application, и далее, что использование Singletons обычно указывает на негибкий дизайн, который не так легко модифицировать и приводит к множеству проблем в будущем. IMHO, сильный контракт, предлагаемый Android API для приложений для разработчиков, является одним из самых привлекательных и приятных аспектов программирования на Android, и помог ему в раннем принятии разработчиков, что привело платформу Android к успеху, который у нее есть сегодня. Предложение использования Singletons неявно отходит от сильного контракта API и, на мой взгляд, ослабляет рамки Android.
Dianne также прокомментировала ниже, упоминая о дополнительном недостатке использования подклассов Application, они могут поощрять или упрощать запись меньшего кода производительности. Это очень верно, и я отредактировал этот ответ, чтобы подчеркнуть важность рассмотрения здесь, и правильного подхода, если вы используете подклассу приложений. Как сообщает Dianne, важно помнить, что ваш класс приложения будет создаваться каждый раз при загрузке вашего процесса (может быть несколько раз сразу, если приложение работает в нескольких процессах!), Даже если процесс загружается только для фонового вещания мероприятие. Поэтому важно использовать класс Application больше как репозиторий для указателей на общие компоненты вашего приложения, а не как место для любой обработки!
Я оставляю вас с нижеследующим списком минусов для синглтонов, которые украдены из предыдущей ссылки StackExchange:
- Неспособность использовать абстрактные или интерфейсные классы;
- Неспособность к подклассу;
- Высокая связь между приложениями (трудно модифицировать);
- Трудно проверить (не может подделывать/издеваться над модульными тестами);
- Трудно распараллеливаться в случае изменчивого состояния (требуется обширная блокировка);
и добавьте свой собственный:
- Нечеткий и неуправляемый контракт на жизнь, не подходящий для разработки Android (или большинства других);