Я написал этот ответ еще в 2009 году, когда Android был относительно новым, а в разработке Android было много неразвитых областей. Я добавил длинное дополнение в конце этого поста, в котором рассматриваются некоторые критические замечания и подробно излагаются мои философские разногласия по поводу использования синглтонов вместо создания подклассов 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;
}
}
Теперь эта медленная инициализация (например, попадание в диск, подключение к сети, блокирование чего-либо и т. Д.) Будет выполняться каждый раз при создании экземпляра приложения! Вы можете подумать, что это только один раз для процесса, и мне все равно придется оплатить стоимость, верно? Например, как упоминает ниже Дайан Хакборн, для вашего процесса вполне возможно создать экземпляр - просто - для обработки события фоновой широковещательной передачи. Если ваша широковещательная обработка не нуждается в этом состоянии, вы потенциально просто зря выполнили целую серию сложных и медленных операций. Ленивое создание экземпляров - вот название игры. Ниже приводится несколько более сложный способ использования Application, который имеет больше смысла для чего угодно, кроме простейшего использования:
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, а не использование синглтонов в качестве более элегантного решения, я бы предпочел, чтобы разработчики использовали синглтоны, если это действительно необходимо, чем вообще не задумываться о производительности и многопоточности, связанной с связыванием состояния с подклассом Application.
ПРИМЕЧАНИЕ 1: Также, как прокомментировал антикафе, для того, чтобы правильно привязать переопределение приложения к вашему приложению, в файле манифеста необходим тег. Опять же, см. Документацию по Android для получения дополнительной информации. Пример:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
ПРИМЕЧАНИЕ 2: user608578 спрашивает ниже, как это работает с управлением жизненными циклами собственных объектов. Я ни в малейшей степени не разбираюсь в использовании нативного кода с Android, и я не могу ответить, как это будет взаимодействовать с моим решением. Если у кого-то есть ответ на этот вопрос, я готов отдать ему должное и помещу информацию в этот пост для максимальной наглядности.
ДОБАВЛЕНИЕ:
Как отмечали некоторые люди, это не решение для постоянного состояния, на что мне, возможно, следовало бы особо подчеркнуть в исходном ответе. Т.е. это не означает, что это решение для сохранения информации о пользователях или другой информации, которая должна сохраняться в течение всего времени жизни приложения. Таким образом, я считаю, что большая часть критики, приведенной ниже, в отношении приложений, которые могут быть убиты в любое время и т. Д., Спорна, поскольку все, что когда-либо требовалось сохранить на диске, не должно храниться через подкласс Application. Он предназначен для хранения временного, легко воспроизводимого состояния приложения (например, вошел ли пользователь в систему) и компонентов, которые являются одним экземпляром (например, диспетчер сети приложения) (НЕ одноэлементный !) в природе.
Дайерман любезно указал на интересный разговор с Рето Мейер и Дайанн Хакборн, в котором использование подклассов Application не рекомендуется в пользу шаблонов Singleton. Соматик также указывал на нечто подобное ранее, хотя в то время я этого не видел. Из-за роли Рето и Дайанны в поддержке платформы Android я не могу добросовестно рекомендовать игнорировать их советы. Что говорят, выходит. Я действительно хочу не согласиться с мнениями, выраженными в отношении предпочтения Singleton над подклассами Application. В моем несогласии я буду использовать концепции, лучше всего объясненные в этом объяснении StackExchange шаблона проектирования Singleton, чтобы В этом ответе мне не нужно определять термины. Я настоятельно рекомендую просмотреть ссылку, прежде чем продолжить. По пунктам:
Дайан заявляет: «Нет причин для создания подкласса от Application. Это ничем не отличается от создания синглтона ...» Это первое утверждение неверно. Для этого есть две основные причины. 1) Класс Application обеспечивает лучшую пожизненную гарантию для разработчика приложения; у него гарантированно будет время жизни приложения. Синглтон НЕ ЯВНО привязан к времени жизни приложения (хотя это эффективно). Это может быть не проблема для вашего среднего разработчика приложений, но я бы сказал, что это именно тот тип контракта, который должен предлагать Android API, и он также обеспечивает гораздо большую гибкость для системы Android, сводя к минимуму время жизни связанных данные. 2) Класс Application предоставляет разработчику приложения единственного держателя экземпляра для состояния, который сильно отличается от держателя одиночного экземпляра состояния. Список различий см. По ссылке объяснения Singleton выше.
Дайан продолжает: «... скорее всего, в будущем вы пожалеете о чем-то, о чем вы обнаружите, что ваш объект Application станет большим запутанным беспорядком из того, что должно быть независимой логикой приложения». Это, конечно, не неправильно, но это не причина выбора подкласса Singleton вместо подкласса Application. Ни один из аргументов Дайан не объясняет, почему использование синглтона лучше, чем подкласс приложения, все, что она пытается установить, - это то, что использование синглтона не хуже подкласса приложения, что, как я считаю, неверно.
Она продолжает: «И это более естественно ведет к тому, как вы должны управлять этими вещами - инициализировать их по запросу». Это игнорирует тот факт, что нет причин, по которым вы не можете инициализировать по запросу, также используя подкласс Application. Опять нет разницы.
Дайан заключает: «В самой структуре есть тонны и тонны синглтонов для всех небольших общих данных, которые она поддерживает для приложения, таких как кеши загруженных ресурсов, пулы объектов и т. Д. Она отлично работает». Я не утверждаю, что использование синглтонов не может работать нормально или не является законной альтернативой. Я утверждаю, что синглтоны не обеспечивают такой прочный контракт с системой Android, как использование подкласса Application, и, кроме того, использование синглтонов обычно указывает на негибкий дизайн, который нелегко изменить, и в дальнейшем приводит к множеству проблем. ИМХО, прочный контракт, который Android API предлагает разработчикам приложений, является одним из самых привлекательных и приятных аспектов программирования с Android, и помог привести к раннему принятию разработчиками, которые привели платформу Android к успеху, который она имеет сегодня. Предложение использовать синглтоны неявно отходит от сильного контракта API и, на мой взгляд, ослабляет платформу Android.
Дайан также прокомментировала ниже, упомянув дополнительный недостаток использования подклассов Application, они могут поощрять или облегчать написание кода с меньшей производительностью. Это очень верно, и я отредактировал этот ответ, чтобы подчеркнуть важность рассмотрения здесь perf и правильного подхода, если вы используете подклассы приложений. Как утверждает Дайан, важно помнить, что ваш класс Application будет создаваться каждый раз при загрузке вашего процесса (может быть несколько раз одновременно, если ваше приложение работает в нескольких процессах!), Даже если процесс загружается только для фоновой трансляции. мероприятие. Поэтому важно использовать класс Application больше как репозиторий для указателей на общие компоненты вашего приложения, а не как место для выполнения какой-либо обработки!
Я оставляю вам следующий список недостатков синглтонов, украденных по предыдущей ссылке на StackExchange:
- Невозможность использования абстрактных или интерфейсных классов;
- Невозможность подкласса;
- Высокая степень сцепления в приложении (сложно изменить);
- Сложно тестировать (не может подделывать / имитировать в модульных тестах);
- Трудно распараллелить в случае изменяемого состояния (требуется обширная блокировка);
и добавить свой:
- Неясный и неуправляемый пожизненный контракт, не подходящий для разработки под Android (или для большинства других);
person
sooniln
schedule
02.04.2009