Когда вызывать контекст активности или контекст приложения?

265

Там было много сообщений о том, что эти два контекста .. Но я все еще не совсем понял

Как я понимаю до сих пор: каждый является экземпляром своего класса, что означает, что некоторые программисты рекомендуют использовать его this.getApplicationContext()как можно чаще, чтобы не «вытекать» из памяти. Это потому, что другой this(получение Activityконтекста экземпляра) указывает на объект Activity, который уничтожается каждый раз, когда пользователь наклоняет телефон или покидает приложение и т. Д. Который, по-видимому, сборщик мусора (GC) не перехватывает и поэтому использует слишком много памяти ..

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

Norfeldt
источник

Ответы:

408

getApplicationContext()почти всегда неправильно. Г-жа Хэкборн (среди прочих) очень четко заявляла, что вы используете толькоgetApplicationContext() тогда, когда знаете, почему вы используете, getApplicationContext()и только когда вам нужно использовать getApplicationContext().

Чтобы быть грубым, «некоторые программисты» используют getApplicationContext()(или getBaseContext(), в меньшей степени), потому что их опыт Java ограничен. Они реализуют внутренний класс (например, a OnClickListenerfor Buttonin in Activity) и нуждаются в Context. Вместо того, чтобы использовать MyActivity.thisдля доступа к внешнему классу this, они используют getApplicationContext()или getBaseContext()для получения Contextобъекта.

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

  • Используйте, getApplicationContext()если вам нужно что-то привязанное к тому, Contextчто само по себе будет иметь глобальную область видимости. Я использую getApplicationContext(), например, WakefulIntentServiceдля статического, WakeLockкоторый будет использоваться для службы. Так как это WakeLockстатично, и мне нужно Contextполучить, PowerManagerчтобы создать его, его безопаснее всего использовать getApplicationContext().

  • Используйте, getApplicationContext()когда вы связываете с a Serviceиз Activity, если вы хотите передать ServiceConnection(т.е. дескриптор привязки) между Activityэкземплярами через onRetainNonConfigurationInstance(). Android внутренне отслеживает привязки через них ServiceConnectionsи содержит ссылки на те, Contextsкоторые создают привязки. Если вы выполняете привязку из Activity, то у нового Activityэкземпляра будет ссылка на тот, у ServiceConnectionкоторого есть неявная ссылка на старый Activity, а старый Activityне удастся собрать.

Некоторые разработчики используют собственные подклассы Applicationдля своих собственных глобальных данных, через которые они получают getApplicationContext(). Это конечно возможно. Я предпочитаю статические члены данных, если только по какой-либо причине у вас может быть только один пользовательский Applicationобъект. Я создал одно приложение, используя пользовательский Applicationобъект, и нашел, что это больно. Мисс Хакборн также согласна с этой позицией .

Вот причины, почему бы не использовать getApplicationContext()везде, где вы идете:

  • Это не полный Context, поддерживающий все, что Activityделает. Различные вещи, которые вы попытаетесь сделать с этим Context, потерпят неудачу, в основном связанные с графическим интерфейсом .

  • Это может привести к утечкам памяти, если Contextfrom getApplicationContext()удерживает что-то созданное вашими вызовами, которые вы не очистите. С помощью Activity, если он что-то держит, как только Activityмусор соберется, все остальное тоже вылетает. ApplicationОбъект остается в течение всей жизни процесса.

CommonsWare
источник
1
Большое спасибо за этот ответ. Еще одна ссылка, которую я нашел до того, как прочитал этот ответ, может также помочь некоторым людям. stackoverflow.com/questions/7298731/… - эта ссылка объясняет мои опасения по поводу утечки памяти.
Норфельдт
27
@Norfeldt: К вашему сведению, ссылка в вашем комментарии ссылается на этот ответ.
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 использует контекст приложения и прямо говорит, что нужно его использовать: Уведомления о тостах

В разделе диалогов руководства по разработке вы видите, что AlertDialog.Builder использует контекст приложения, а затем индикатор выполнения использует контекст действия. Это не объясняется Google. Диалоги

Кажется, что хорошая причина использовать контекст приложения - это когда вы хотите обрабатывать изменения конфигурации, например, изменение ориентации, и вы хотите сохранить объекты, которым нужен контекст, такой как Views. Если вы посмотрите здесь: Изменения во время выполнения Существует осторожность при использовании контекста активности, который может вызвать утечку. Этого можно избежать с помощью контекста приложения с представлениями, которые должны быть сохранены (по крайней мере, это мое понимание). В приложении, которое я пишу, я намерен использовать контекст приложения, потому что я пытаюсь удерживать некоторые представления и другие вещи при изменении ориентации, и я все еще хочу, чтобы действие было уничтожено и воссоздано при изменении ориентации. Таким образом, я должен использовать контекст приложения, чтобы не вызвать утечку памяти (см. Предотвращение утечек памяти). Мне кажется, что есть много веских причин использовать контекст приложения вместо контекста активности, и мне кажется, что вы будете использовать его чаще, чем контекст активности. Это то, что многие книги по Android, которые я просмотрел, похоже, делают, и это то, что делают многие примеры Google, которые я видел.

Документация Google действительно создает впечатление, что использование контекста приложения в большинстве случаев прекрасно, и на самом деле это происходит чаще, чем использование контекста активности в их примерах (по крайней мере, в тех примерах, которые я видел). Если использование контекста приложения на самом деле является такой проблемой, то Google действительно нужно уделять больше внимания этому. Им нужно прояснить это, и они должны переделать некоторые из своих примеров. Я бы не стал винить в этом неопытных разработчиков, поскольку авторитет (Google) действительно заставляет его выглядеть так, будто это не проблема использовать контексты приложения.

Энди Джей
источник
5
Я полностью согласен. Ответ CommonsWare стал для меня неожиданностью. Я рад, что нашел этот вопрос, потому что в документации Google говорится, что использование getApplicationContext может быть настолько опасным.
Стив Шварц
38

Я использовал эту таблицу как руководство для того, когда использовать различные типы контекста, такие как контекст приложения (то есть:) getApplicationContext()и контекст действия , а также контекст BroadcastReceiver :

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

Все заслуги идут к оригинальному автору здесь для получения дополнительной информации.

CommonSenseCode
источник
11

Какой контекст использовать?

Существует два типа контекста:

  1. Контекст приложения связан с приложением и всегда будет одинаковым на протяжении всего жизненного цикла приложения - он не меняется. Поэтому, если вы используете Toast, вы можете использовать контекст приложения или даже контекст активности (оба), потому что toast может отображаться в любом месте вашего приложения и не привязан к конкретному окну. Но есть много исключений, одно исключение, когда вам нужно использовать или передавать контекст действия.

  2. Контекст действия связан с действием и может быть уничтожен, если действие уничтожено - может быть несколько действий (более чем вероятно) с одним приложением. А иногда вам абсолютно необходим дескриптор контекста активности. Например, если вы запускаете новое действие, вам нужно использовать контекст действия в его намерении, чтобы новое действие запуска было связано с текущим действием с точки зрения стека действий. Тем не менее, вы также можете использовать контекст приложения для запуска нового действия, но тогда вам нужно установить флаг, Intent.FLAG_ACTIVITY_NEW_TASKчтобы рассматривать его как новую задачу.

Давайте рассмотрим несколько случаев:

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

  • getBaseContext() предлагает контекст деятельности.

  • getApplication() предлагает контекст приложения.

  • getApplicationContext() также предлагает контекст приложения.

Для получения дополнительной информации, пожалуйста, проверьте эту ссылку .

Зохра Хан
источник
Как насчет случая, когда необходимо отобразить AlertDialog в приложении, например, асинхронный процесс, показывающий результат. Примером этого может быть : пользователь нажимает на загрузку, при этом запускается запрос на загрузку downloadmanager, и когда готовый сигнал получен, должно отображаться диалоговое окно, например «Что вы хотите сделать с этой загрузкой?». Мое (хакерское) решение сохраняет самое последнее Activityв static Applicationклассе и запрашивает текущее, Activityкогда загрузка завершена. Однако я сомневаюсь, что это правильная реализация. TL; DR Как отобразить AlertDialog в любом месте приложения?
CybeX
@KGCybeX Если вы хотите отображать что-либо и в любом месте вашего приложения после завершения загрузки, вы должны вручную зарегистрировать получателя широковещательной рассылки в своей активности, который прослушивает конкретное сообщение, которое будет передаваться вашей службой загрузки, и делать все, что вы захотите после получения сообщения, или прикрепить Ваша деятельность в этой службе напрямую.
ExiRouS
6

Мне было интересно, почему бы не использовать Application Context для каждой операции, которую он поддерживает. В конце концов, это снижает вероятность утечки памяти и пропускает нулевую проверку для getContext () или getActivity () (при использовании внедренного контекста приложения или при получении статическим методом из Application). Утверждения, подобные мисс Хакборн об использовании контекста приложения только в случае необходимости, не кажутся мне убедительными без объяснения причин. Но, похоже, я нашел несогласных, почему:

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

Потому что не гарантируется, что все операции, описанные как поддерживаемые контекстом приложения в таблице ниже, будут работать на всех устройствах Android! введите описание изображения здесь

Malachiasz
источник
4

Два замечательных примера использования контекста «Активность» по сравнению с контекстом приложения - при отображении сообщения Toast или встроенного диалогового окна, поскольку использование контекста приложения вызовет исключение:

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

или

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

Оба из них нуждаются в информации из контекста действия, которая не предоставляется в контексте приложения.

SBerg413
источник
5
Хм .. Какую версию Android OS вы тестировали? Я тестировал на 4.4.4, и он работает хорошо. Кроме того, как отметил @Andi Jay, официальный документ разработчика Android использовал контекст приложения в своем примере кода. developer.android.com/guide/topics/ui/notifiers/…
김준호
1
@ Китайское имя, да, это может сработать, но когда-нибудь в будущем это приложение также будет аварийно завершено. Несколько раз.
Оджонугва Джуд Очалифу
1
Когда я использую контекст Activity в Toast, он теряет память!
Джемшит Искендеров
3

Контекст приложения жить до тех пор пока ваше приложение живо только и это не зависит от деятельности жизненного цикла , но, контекст объекта держать долгоживущие . Если объект, который вы используете временно, тогда использует контекст приложения и контекст действия, который полностью противоположен контексту приложения.

Ганеш Катикар
источник