Цю відповідь я написав ще у09 році, коли Android був порівняно новим, і в розробці Android було багато не добре сформованих областей. Я додав довге доповнення в нижній частині цього поста, торкаючись деякої критики та деталізуючи філософську незгоду, яку я маю з використанням Singletons, а не з додатком підкласифікації. Прочитайте його на свій страх і ризик.
ОРИГІНАЛЬНИЙ ВІДПОВІДЬ:
Більш загальна проблема, з якою ви стикаєтесь - це збереження стану в кількох видах діяльності та всіх частинах вашої програми. Статична змінна (наприклад, синглтон) є загальним способом досягнення цього 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, і я не кваліфікований, щоб відповісти, як це буде взаємодіяти з моїм рішенням. Якщо у когось є відповідь на це, я готовий кредитувати їх і розміщувати інформацію в цій посаді для максимальної наочності.
ДОДАТИ:
Як зазначали деякі люди, це не є рішенням для стійкого стану, я, можливо, мав би більше підкреслити в оригінальній відповіді. Тобто це не означає, що це рішення для збереження користувача чи іншої інформації, яка має зберігатися впродовж життя. Таким чином, я вважаю, що більшість критичних даних нижче стосуються того, що програми вбиваються в будь-який час тощо. Він призначений для вирішення для зберігання тимчасового стану програми, що легко перетворюється (наприклад, користувач увійшов, наприклад) та компонентів, що є єдиними екземплярами (наприклад, менеджер мережі додатків) ( НЕ одиночний!).
Дейерман був досить люб'язним, щоб вказати на цікаву розмову з Рето Мейєром та Діанна Хакборн, в якій використання підкласів програми не рекомендується використовувати на користь моделей Singleton. Соматік також зазначив щось подібне раніше, хоча я цього не бачив. Через ролі Рето та Діанна в підтримці платформи Android я не можу добросовісно рекомендувати ігнорувати їхні поради. Те, що вони кажуть, іде. Я не хочу погоджуватися з думками, висловленими щодо переваги Singleton перед підкласами Application. В свою незгоду я буду використовувати поняття, найкраще пояснені в цьому поясненні StackExchange схеми дизайну Singleton, так що мені не доведеться визначати терміни у цій відповіді. Я настійно закликаю прокрутити посилання, перш ніж продовжувати. По пункту:
Діанна заявляє: "Немає підстав для підкласу з Application. Це нічим не відрізняється від складання одиночного ..." Ця перша претензія є неправильною. Для цього є дві основні причини. 1) Клас програми забезпечує кращу довічну гарантію для розробника програми; гарантовано тривалість роботи програми. Однотонний НЕ ЕКСПЛІКАТНО пов'язаний із терміном служби програми (хоча це ефективно). Це може бути проблемою для середнього розробника додатків, але я заперечую, що це саме той тип контрактів, який має пропонувати API для Android, і він забезпечує набагато більшу гнучкість і для системи Android, скорочуючи термін експлуатації пов'язаних дані. 2) Клас програми надає розробнику програми єдиний власник екземпляра для стану, що сильно відрізняється від державника-власниці Сінглтона. Для переліку відмінностей див. Посилання на пояснення Singleton вище.
Діанна продовжує: "... це, ймовірно, буде щось, про що ви пошкодуєте в майбутньому, коли ви виявите, що ваш об'єкт Application стає таким великим заплутаним безладом того, що має бути незалежною логікою програми". Це, звичайно, невірно, але це не є причиною вибору Singleton над підкласом Application. Жоден з аргументів Діани не дає причини, що використання Singleton краще, ніж підклас Application, все, що вона намагається встановити, полягає в тому, що використання Singleton не гірше ніж підклас Application, який, на мою думку, є помилковим.
Вона продовжує: "І це природніше призводить до того, як ви повинні керувати цими речами - ініціалізуючи їх на вимогу". Це ігнорує той факт, що немає причини, щоб ви не могли ініціалізувати на вимогу, використовуючи також підклас програми. Знову немає різниці.
Діанна закінчується "У самій структурі є тонни і тони синглів для всіх малопоширених даних, які вони підтримують для додатка, таких як кеші завантажених ресурсів, пули об'єктів тощо. Це чудово працює". Я не стверджую, що використання Singletons не може спрацювати нормально або не є законною альтернативою. Я стверджую, що Singletons не забезпечує такий міцний контракт із системою Android, як використання підкласу Application, і, крім того, що використання Singletons, як правило, вказує на негнучку конструкцію, яка не легко змінюється, і призводить до багатьох проблем у дорозі. IMHO, сильний контракт, який Android API пропонує розробникам додатків, є одним із найпривабливіших та найприємніших аспектів програмування з Android, а також допоміг до раннього прийняття розробника, що призвело платформу Android до успіху, який вона має сьогодні.
Дайанн також прокоментував нижче, згадуючи додатковий недолік використання підкласів програми, вони можуть заохочувати або полегшувати написання менш ефективного коду. Це дуже вірно, і я відредагував цю відповідь, щоб підкреслити важливість розгляду тут перф і правильного підходу, якщо ви використовуєте підкласинг додатків. Як зазначає Діанне, важливо пам’ятати, що ваш клас програми буде інстанціюватися кожного разу, коли ваш процес завантажується (може бути кілька разів одночасно, якщо ваша програма працює в декількох процесах!), Навіть якщо процес завантажується лише для фонового мовлення подія. Тому важливо використовувати клас Application більше як сховище для покажчиків на спільні компоненти вашої програми, а не як місце для будь-якої обробки!
Я залишаю вам наступний список мінусів для Singletons, викрадений з попереднього посилання StackExchange:
- Неможливість використання абстрактних або інтерфейсних класів;
- Нездатність підкласу;
- Висока зв'язок у додатку (важко модифікувати);
- Важко перевірити (не можна підробити / знущатися в одиницях тестів);
- Важко паралелізувати у разі зміни стану (потребує великого блокування);
і додати моє власне:
- Неясний і некерований довічний контракт, не підходить для розробки Android (або більшості інших);