Коли викликати контекст діяльності АБО контекст програми?


265

Було багато повідомлень про те, що ці два контексти. Але я все ще не розумію це

Як я це розумію до цих пір: кожен - це екземпляр свого класу, що означає, що деякі програмісти рекомендують вам використовувати this.getApplicationContext()якомога частіше, щоб не "витікати" жодної пам'яті. Це тому, що інше this(отримання Activityконтексту екземпляра) вказує на Activityте, що знищується кожного разу, коли користувач нахиляє телефон або залишає додаток тощо. Що, мабуть, збирач сміття (GC) не вловлює і тому використовує занадто багато пам'яті ..

Але чи може хто-небудь придумати декілька дійсно хороших прикладів кодування, де було б правильно використовувати this(отримуючи контекст поточного Activityекземпляра), а контекст програми буде марним / неправильним?

Відповіді:


408

getApplicationContext()майже завжди помиляється. Г - жа Hackborn (серед інших) було дуже чітко , що ви тільки використовувати , getApplicationContext()коли ви знаєте , чому ви використовуєте getApplicationContext()і тільки тоді , коли вам потрібно використовувати getApplicationContext().

Щоб бути тупими, "деякі програмісти" використовують getApplicationContext()(або getBaseContext()в меншій мірі), оскільки їхній досвід Java обмежений. Вони реалізують внутрішній клас (наприклад, a OnClickListenerfor a Buttonin an Activity) і потребують a Context. Замість того, щоб використовувати, MyActivity.thisщоб потрапити на зовнішній клас ' this, вони використовують getApplicationContext()або getBaseContext()для отримання Contextоб'єкта.

Ви користуєтесь лишеgetApplicationContext() тоді, коли знаєте, що вам потрібно Contextщось, що може прожити довше, ніж будь-яке інше, що, можливо, у Contextвас є. Сценарії включають:

  • Використовуйте, getApplicationContext()якщо вам потрібно щось прив’язане до того, Contextщо саме по собі матиме глобальний розмах. Я використовую getApplicationContext(), наприклад, в WakefulIntentService, для статики, WakeLockяка буде використана для послуги. Оскільки WakeLockце статично, і мені потрібно Contextдосягти, PowerManagerщоб створити його, це найбезпечніше використовувати getApplicationContext().

  • Використовуйте, getApplicationContext()коли ви прив'язуєте до а Serviceз Activity, якщо ви хочете передати ServiceConnection(тобто, ручку до прив'язки) між Activityекземплярами через onRetainNonConfigurationInstance(). Android внутрішньо відстежує прив’язки через них ServiceConnectionsі містить посилання на ті, Contextsщо створюють прив’язки. Якщо ви пов'язуєте з Activity, тоді новий Activityекземпляр матиме посилання на той, ServiceConnectionякий має неявне посилання на старий Activity, а старий Activityне може бути зібраний сміття.

Деякі розробники використовують власні підкласи Applicationдля власних глобальних даних, через які вони отримують getApplicationContext(). Це, безумовно, можливо. Я віддаю перевагу статичним членам даних, якщо з будь-якої іншої причини, ніж у вас, може бути лише один спеціальний Applicationоб'єкт. Я створив один додаток за допомогою спеціального Applicationоб’єкта і виявив, що це боляче. Пані Хакборн також погоджується з цією позицією .

Ось причини, чому не використовувати getApplicationContext()куди б то не було:

  • Це не повне Context, що підтримує все, що Activityробить. Не вдасться зробити різні речі, які ви спробуєте зробити з цим Context, в основному пов'язані з графічним інтерфейсом .

  • Це може створити витоки пам'яті, якщо Contextз getApplicationContext()утримує щось, створене вашими дзвінками на ньому, що ви не очищаєте. Якщо Activity, якщо воно тримається за щось, як тільки Activityзбирається сміття, все інше теж вимиває. ApplicationОб'єкт залишається в протягом усього життя процесу.


1
Дуже дякую за цю відповідь. Ще одне посилання, яке я знайшов, перш ніж прочитати цю відповідь, також може допомогти деяким людям. stackoverflow.com/questions/7298731/… - це посилання пояснює мою занепокоєння щодо витоку пам'яті.
Норфельдт

27
@Norfeldt: FYI, посилання у вашому коментарі посилається на цю відповідь.
CommonsWare

2
дякую .. це було посилання: stackoverflow.com/questions/5796611/… це описує витік пам’яті, який я побоювався отримати за допомогою цього
Norfeldt

6
@djaqeel: Остання частина вашої цитати майже правдива. Краще висловлюватись як "не надавати контексту діяльності тому, що буде жити довше, ніж буде діяти, наприклад статичному учаснику даних". Однак ви все ще використовуєте лише getApplicationContext()тоді, коли точно знаєте, навіщо вам це потрібно в тій чи іншій ситуації. Надуття макета? Використовуйте активність. Прив’язання до служби, де вам потрібна ця прив'язка, щоб пережити зміну конфігурації? Використовуйте getApplicationContext(), тому прив'язка не прив’язана до Activityекземпляра.
CommonsWare

7
@Sever: Я висвітлюю це у своїй відповіді. У Дейва Сміта також є чудова публікація в блозі, що висвітлює контексти: doubleencore.com/2013/06/context Його короткий абзац: "У більшості випадків використовуйте контекст, доступний вам безпосередньо з компонента, що додає, в якому ви працюєте. Ви можете безпечно тримати посилання на нього до тих пір, поки ця посилання не виходить за межі життєвого циклу цього компонента. Як тільки вам потрібно зберегти посилання на контекст від об'єкта, який живе за межами вашої діяльності чи послуги, навіть тимчасово перемкніть цю посилання, яку ви збережете над контекстом програми. "
CommonsWare

48

Я думаю, що на сайті SDK є багато матеріалів, які погано зафіксовані, це один із них. Твердження, яке я висловлю, полягає в тому, що, здається, краще використовувати контекст програми за замовчуванням і використовувати контекст діяльності лише тоді, коли вам це потрібно. Єдине місце, де я коли-небудь бачив, що вам потрібен контекст діяльності, - це діалог про хід виконання. SBERG412 стверджує, що потрібно використовувати контекст активності для тостів, але документи Android чітко показують, що контекст програми використовується. Я завжди використовував контекст програми для тостів через цей приклад Google. Якщо це неправильно, то Google тут кинув м'яч.

Ось більше, щоб подумати і переглянути:

Для повідомлення про тости Посібник Google Dev використовує контекст програми та чітко говорить про його використання: Toast Notifications

У розділі діалогів посібника Dev ви бачите, що AlertDialog.Builder використовує контекст програми, а потім рядок виконання використовує контекст діяльності. Google не пояснює це. Діалоги

Здається, що вагомою причиною використання контексту програми є те, коли ви хочете обробити зміни конфігурації, як зміна орієнтації, і ви хочете зберегти об'єкти, яким потрібен контекст, як Views. Якщо ви подивитесь тут: Зміни часу запуску Існує обережність щодо використання контексту діяльності, який може створити витік. Цього можна уникнути в контексті програми із думками, які слід зберегти (принаймні, це я розумію). У програмі, про яку я пишу, я маю намір використовувати контекст програми, тому що я намагаюся дотримуватися деяких поглядів та інших речей щодо зміни орієнтації, і я все ще хочу, щоб діяльність була знищена та відтворена щодо змін орієнтації. Таким чином, я повинен використовувати контекст програми, щоб не викликати витоку пам'яті (див. Уникнення витоків пам'яті). Мені здається, є достатньо вагомих причин використовувати контекст програми замість контексту діяльності, і мені майже здається, ви б використовували його частіше, ніж контекст діяльності. Ось, як здається, робиться багато книг для Android, які я пережив, і це те, з чого я бачив більшість прикладів Google.

З документації Google насправді здається, що використання контексту додатків ідеально добре в більшості випадків, і насправді з’являється частіше, ніж використання контексту діяльності у своїх прикладах (принаймні, приклади, які я бачив). Якщо реально така проблема використовувати контекст програми, то Google дійсно повинен приділяти цьому більше уваги. Їм потрібно зрозуміти, і вони повинні переробити деякі свої приклади. Я б не звинувачував це повністю у недосвідчених розробниках, оскільки влада (Google) насправді робить це схожим на те, що використовувати проблеми контексту додатків не проблема.


5
Я повністю згоден. Відповідь CommonsWare стала для мене трохи несподіванкою. Я радий, що знайшов це питання, тому що в документації Google він говорить про те, що використання getApplicationContext може бути настільки небезпечним.
Стів Шварч

38

Я використовував цю таблицю як настанову щодо використання різних типів контексту, таких як контекст програми (тобто:) getApplicationContext()та контекст діяльності , також контекст BroadcastReceiver :

введіть тут опис зображення

Усі достоїнства переходять до оригінального автора тут для отримання додаткової інформації.


11

Який контекст використовувати?

Існує два типи контексту:

  1. Контекст програми пов'язаний із програмою і завжди буде однаковим протягом усього життя програми - він не змінюється. Отже, якщо ви використовуєте Toast, ви можете використовувати контекст програми або навіть контекст діяльності (обидва), оскільки тост може відображатися з будь-якого місця у вашій програмі і не прикріплений до певного вікна. Але є багато винятків, один виняток - коли вам потрібно використовувати або передавати контекст діяльності.

  2. Контекст діяльності пов'язаний з діяльністю і може бути знищений, якщо активність знищена - з однією програмою може бути кілька дій (більш ніж ймовірно). І іноді вам абсолютно потрібна обробка контексту діяльності. Наприклад, якщо ви запускаєте нову активність, вам потрібно використовувати контекст активності в її намірі, щоб нова активізація запуску була підключена до поточної активності з точки зору стеку активності. Однак ви можете також використовувати контекст програми для запуску нової діяльності, але тоді вам потрібно встановити прапор, Intent.FLAG_ACTIVITY_NEW_TASKщоб розглянути його як нове завдання.

Розглянемо деякі випадки:

  • MainActivity.this посилається на контекст MainActivity, який розширює клас діяльності, але базовий клас (активність) також розширює клас контексту, тому його можна використовувати для пропонування контексту діяльності.

  • getBaseContext() пропонує контекст діяльності.

  • getApplication() пропонує контекст програми.

  • getApplicationContext() також пропонує контекст програми.

Для отримання додаткової інформації перейдіть за цим посиланням .


Як щодо випадку, коли потрібно відобразити AlertDialog у додатку, наприклад, процес асинхронізації, що показує результат. Прикладом цього може бути : користувач натискає на завантаження, це викликає запит на завантаження downloadmanager, і коли отриманий сигнал буде отриманий, він повинен відобразити діалогове вікно, наприклад, "Що ви хочете зробити з цим завантаженням?". Моє (злому) рішення зберігає найновіші Activityв static Applicationкласі та запитує поточну, Activityколи завантаження завершено. Однак я сумніваюся, що це правильна реалізація. TL; DR Як відобразити AlertDialog де-небудь у додатку?
CybeX

@KGCybeX Якщо ви хочете відобразити що-небудь у вашій програмі, коли завантаження завершиться, вам слід вручну зареєструвати приймач широкомовної програми щодо вашої діяльності, який прослуховує певне повідомлення про те, що ваша служба завантаження транслюватиметься та робити все, що ви хочете після отримання повідомлення, або додайте Ваша діяльність безпосередньо в цій службі.
ExiRouS

6

Мені було цікаво, чому б не використовувати Application Context для кожної операції, яку він підтримує. Врешті-решт це знижує ймовірність витоку пам’яті та відсутності перевірки нуля для getContext () або getActivity () (при використанні ін'єкційного контексту програми або придбаного за допомогою статичного методу з Application). Висловлювання, як те, що пані Хакборн використовувати контекст програми лише за потреби, не здаються переконливими для мене без пояснення того, чому. Але, схоже, я знайшов одяг, чому:

виявили, що в деяких версіях Android / комбінаціях пристроїв є проблеми, які не відповідають цим правилам. Наприклад, якщо у мене є BroadcastReceiver, який передається Context, і я перетворюю цей контекст у контекст програми, а потім намагаюся викликати registerReceiver () у програмі Context, є багато випадків, коли це працює добре, але також багато випадків, коли я отримую збій через ReceiverCallNotAllowedException. Ці збої трапляються у широкому діапазоні версій Android від API 15 до 22. https://possiblemobile.com/2013/06/context/#comment-2443283153

Оскільки не гарантується, що всі операції, описані як підтримувані контекстом додатків у таблиці нижче, працюватимуть на всіх пристроях Android! введіть тут опис зображення


4

Два чудових прикладу, коли слід використовувати контекст "Діяльність" та "контекст програми", коли відображається повідомлення Toast або вбудоване діалогове повідомлення, оскільки використання контексту програми спричинить виняток:

ProgressDialog.show(this, ....);

або

Toast t = Toast.makeText(this,....);

Вони потребують інформації з контексту діяльності, яка не надається в контексті програми.


5
Гм .. Яку версію ОС Android ви протестували? Я тестував 4.4.4, і він працює добре. Крім того, як зазначав @Andi Jay, офіційний документ розробника Android використовував контекст програми у своєму зразковому коді. developer.android.com/guide/topics/ui/notifiers/…
김준호

1
@ Китайське ім'я, так, це може спрацювати, але колись у майбутньому цього додатка воно також вийде з ладу. Сталося зі мною кілька разів.
Ojonugwa Jude Ochalifu

1
Коли я використовую контекст діяльності в Toast, це просочує пам'ять!
Джемшит Іскендеров

3

Контекст програми живий, поки ваша програма не є живою, і це не залежить від життєвого циклу діяльності, але контекст зберігає об'єкт довговічним . Якщо об'єкт, яким ви користуєтесь тимчасовим, використовуйте контекст програми та контекст активності, який використовується повністю в контексті програми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.