Источник
Тема контекста в Android многих сбивает с толку.Люди просто знают, что контекст необходим довольно часто для выполнения основных задач в Android.Люди иногда впадают в панику, потому что они пытаются выполнить какую-то операцию, которая требует контекста, и они не знают, как «получить» правильный контекст.Я собираюсь попытаться демистифицировать идею контекста в Android.Полное рассмотрение проблемы выходит за рамки этого поста, но я постараюсь дать общий обзор, чтобы у вас было представление о том, что такое контекст и как его использовать.Чтобы понять, что такое контекст, давайте взглянем на исходный код:
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/content/Context.java
Что такое контекст?
Ну,Сама документация дает довольно простое объяснение: класс Context является «интерфейсом к глобальной информации о среде приложения».
Сам класс Context объявляется как абстрактный класс, реализация которого обеспечивается ОС Android.Документация также предусматривает, что Контекст «… разрешает доступ к ресурсам и классам, относящимся к конкретному приложению, а также к дополнительным вызовам для операций на уровне приложения, таких как действия по запуску, широковещательные и приемные намерения и т. д.».
Теперь вы можете очень хорошо понять, почему имя называется Context.Это потому, что это просто так.Контекст предоставляет ссылку или ловушку, если хотите, для Действия, Сервиса или любого другого компонента, тем самым связывая его с системой, обеспечивая доступ к глобальной среде приложения.Другими словами: Контекст дает ответ на вопрос компонентов: «Где я, черт возьми, по отношению к приложению в целом и как я могу получить доступ к общему приложению и общаться с ним?». Если все это немного сбивает с толку, быстроПосмотрите на методы, предоставляемые классом Context, вы получите некоторые дополнительные сведения о его истинной природе.
Вот случайная выборка этих методов:
getAssets()
getResources()
getPackageManager()
getString()
getSharedPrefsFile()
Что общего у всех этих методов?Все они позволяют любому, кто имеет доступ к контексту, иметь доступ к ресурсам всего приложения.
Контекст, другими словами, перехватывает компонент, имеющий ссылку на него, в остальную среду приложения.Например, ресурсы (например, папка «/ assets» в вашем проекте) доступны по всему приложению при условии, что Activity, Сервис или кто-либо еще знает, как получить доступ к этим ресурсам.То же самое касается getResources()
, который позволяет делать такие вещи, как getResources().getColor()
, который подключит вас к ресурсу colors.xml
(не говоря уже о том, что aapt разрешает доступ к ресурсам через код Java, это отдельная проблема).
В результате, Context
- это то, что обеспечивает доступ к системным ресурсам и то, что подключает компоненты к «большему приложению». Давайте рассмотрим подклассы Context
, классы, которые обеспечивают реализациюабстрактный класс Context
. Наиболее очевидным классом является класс Activity
. Activity
наследуется от ContextThemeWrapper
, который наследуется от ContextWrapper
, который наследуется от самого Context
. Эти классы полезны для понимания, чтобы понятьвещи на более глубоком уровне, но на данный момент достаточно знать, что ContextThemeWrapper
и ContextWrapper
в значительной степени похожи на то, как они звучат. Они реализуют абстрактные элементы самого класса Context
, «оборачивая» контекст (фактическийконтекст) и делегирование этих функций этому контексту. Пример полезен - в классе ContextWrapper
абстрактный метод getAssets
из класса Context
реализован следующим образом:
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
mBase
это просто поле, установленное конструктором для определенного контекста, поэтому контекст переносится, и ContextWrapper
делегирует его реализациюметода getAssets для этого контекста.Давайте вернемся к рассмотрению класса Activity
, который в конечном итоге наследуется от Context
, чтобы увидеть, как все это работает.
Вы, наверное, знаете, что такое активность, бОбзор - это, по сути, «единственное, что может сделать пользователь. Он заботится о предоставлении окна для размещения пользовательского интерфейса, с которым взаимодействует пользователь ».
Разработчики, знакомые с другими API-интерфейсами, и даже не разработчики могут воспринимать его как «экран». Это технически неточно, но для наших целей это не имеет значения. Так как же взаимодействуют Activity
и Context
и что именно происходит в их наследственных отношениях?
Опять же, полезно взглянуть на конкретные примеры. Мы все знаем, как начать деятельность. При условии, что у вас есть «контекст», из которого вы начинаете действие, вы просто звоните startActivity(intent)
, где Intent описывает контекст, из которого вы запускаете действие, и действие, которое вы хотите начать. Это знакомый startActivity(this, SomeOtherActivity.class)
.
А что такое this
? this
- это ваша активность, потому что класс Activity
наследуется от Context
. Полный цикл выглядит так: когда вы вызываете startActivity
, в конечном итоге класс Activity
выполняет что-то вроде этого:
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
Таким образом, он использует execStartActivity
из Instrumentation
класса (фактически из внутреннего класса в Instrumentation
, называемого ActivityResult
).
На этом этапе мы начинаем изучать внутренние компоненты системы.
Здесь ОС фактически обрабатывает все. Так, как Инструментарий точно начинает Деятельность? Итак, параметр this
в методе execStartActivity
, описанном выше, - это ваша активность, т.е. контекст, и execStartActivity
использует этот контекст.
Обзор 30 000 заключается в следующем: класс Instrumentation отслеживает список операций, которые он отслеживает для выполнения своей работы. Этот список используется для координации всех действий и обеспечения бесперебойной работы потока операций.
Есть некоторые операции, которые я не полностью изучил, для координации потока и проблем процесса. В конечном счете, ActivityResult
использует собственную операцию - ActivityManagerNative.getDefault().startActivity()
, которая использует Context
, который вы передали при вызове startActivity
. Контекст, который вы передали, используется для «разрешения намерений», если это необходимо. Умышленное разрешение - это процесс, с помощью которого система может определить цель намерения, если оно не предоставлено. (Смотрите руководство здесь для более подробной информации).
И для того, чтобы Android это делал, ему необходим доступ к информации, предоставляемой Context
. В частности, система должна иметь доступ к ContentResolver
, чтобы она могла «определять MIME-тип данных намерения».
Весь этот бит о том, как startActivity
использует контекст, был немного сложным, и я сам не до конца понимаю внутренности. Моя основная цель состояла в том, чтобы просто показать, как нужно обращаться к ресурсам всего приложения, чтобы выполнять многие операции, которые важны для приложения. Context
- это то, что обеспечивает доступ к этим ресурсам.
Более простой пример может быть Views. Мы все знаем, что вы создаете пользовательский вид, расширяя RelativeLayout
или какой-либо другой класс View
, вы должны предоставить конструктор, который принимает Context
в качестве аргумента. Когда вы создаете экземпляр своего пользовательского представления, вы переходите в контекст.
Зачем? Поскольку представление должно иметь доступ к темам, ресурсам и другим деталям конфигурации представления.
Просмотр конфигурации на самом деле отличный пример. Каждый контекст имеет различные параметры (поля в реализациях Context
), которые устанавливаются самой ОС для таких вещей, как размер или плотность отображения. Легко понять, почему эта информация важна для настройки представлений и т. Д.
Последнее слово:
По какой-то причине люди, плохо знакомые с Android (и даже не новички), похоже, полностью забывают об объектно-ориентированном программировании, когда дело доходит до Android. По какой-то причине люди пытаются склонить свои разработки под Android к заранее продуманным парадигмам или изученному поведению.
У Android есть своя собственная парадигма иd определенный шаблон, который на самом деле вполне согласован, если отпустить ваши предвзятые представления и просто прочитать документацию и руководство разработчика. Моя реальная точка зрения, однако, хотя «получение правильного контекста» может иногда быть хитрым, люди неоправданно паникуют, потому что сталкиваются с ситуацией, когда им нужен контекст, и думают, что его нет. Еще раз, Java является объектно-ориентированным языком с дизайном наследования.
Вы только «имеете» контекст внутри своей Деятельности, потому что ваша активность сама наследуется от Контекста. В этом нет ничего волшебного (кроме всего того, что сама ОС делает для установки различных параметров и для правильной «настройки» вашего контекста). Таким образом, оставляя в стороне проблемы с памятью / производительностью (например, удерживая ссылки на контекст, когда вам это не нужно, или делая это таким образом, что это имеет негативные последствия для памяти и т. Д.), Context - это объект, подобный любому другому, и его можно передавать как и любой POJO (обычный старый объект Java).
Иногда вам может потребоваться сделать что-то умное для извлечения этого контекста, но любой обычный класс Java, который расширяется из ничего, кроме самого объекта, может быть написан так, чтобы иметь доступ к контексту; просто предоставьте открытый метод, который принимает контекст, а затем используйте его в этом классе по мере необходимости. Это не было задумано как исчерпывающий подход к Context или внутренним компонентам Android, но я надеюсь, что это немного поможет в демистификации Context.