Context
Context – это объект, который предоставляет доступ к базовым функциям приложения: доступ к ресурсам, к файловой системе, вызов активности и т.д. Activity является подклассом Context, поэтому в коде мы можем использовать её как ИмяАктивности.this (напр. MainActivity.this), или укороченную запись this. Классы Service, Application и др. также работают с контекстом.
Доступ к контексту можно получить разными способами. Существуют такие методы как getApplicationContext(), getContext(), getBaseContext() или this, который упоминался выше, если используется в активности.
На первых порах не обязательно понимать, зачем он нужен. Достаточно помнить о методах, которые позволяют получить контекст и использовать их в случае необходимости, когда какой-нибудь метод или конструктор будет требовать объект Context в своих параметрах.
В свою очередь Context имеет свои методы, позволяющие получать доступ к ресурсам и другим объектам.
- getAssets()
- getResources()
- getPackageManager()
- getString()
- getSharedPrefsFile()
Возьмём к примеру метод getAssets(). Ваше приложение может иметь ресурсы в папке assets вашего проекта. Чтобы получить доступ к данным ресурсам, приложение использует механизм контекста, который и отвечает доступность ресурсов для тех, кто запрашивает доступ — активность, служба и т.д. Аналогично происходит с методом getResources. Например, чтобы получить доступ к ресурсу цвета используется конструкция getResources().getColor(), которая может получить доступ к данным из файла res/colors.xml.
Таким образом, создавая, например, вторую активность, мы можем сразу обеспечить ей доступ к своим ресурсам, так как активность относится к контексту. При создании собственных компонентов View также используется контекст в конструкторах, так как компонент тоже может использовать ваши ресурсы. При создании собственных классов, если вам нужно будет обращаться к контексту, то необходимо создать конструктор:
// В классе сделайте конструктор, куда будет передаваться контекст: public MyClass(Context context) < this.context = context; >// в активности MyClass cat = new MyClass(this);
Через контекст можно узнать практически всю информацию о вашем приложении — имя пакета, класса и т.п.
// имя вашего пакета String info = this.getApplicationInfo().packageName;
Тем не менее, следует различать контекст в разных ситуациях. Допустим, у вас есть приложение с несколькими активностями. В манифесте можно прописать используемую тему как для всего приложения, так и для каждой активности в отдельности. Соответственно, выбор контекста повлияет на результат. Как правило, при использовании собственной темы предпочтительнее использовать контекст активности, а не приложения.
Очень часто начинающие программисты впадают в ступор, когда ключевое слово this не работает в анонимных классах, например, при щелчке кнопки. В этом случае, используйте полное имя класса перед ним.
Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View view) < Toast.makeText(MainActivity.this, "Мяу", Toast.LENGTH_SHORT).show(); > >);
При создании адаптеров для списков также обращаются к контексту.
if (convertView == null) < convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, parent, false); >
Или ещё пример для адаптера в фрагменте ListFragment:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) < String[] names; // имена котов // бла-бла-бла, т.е. мяу-мяу-мяу ArrayAdapteradapter = new ArrayAdapter<>( inflater.getContext(), android.R.layout.simple_list_item_1, names); setListAdapter(adapter); return super.onCreateView(inflater, container, savedInstanceState); >
Здесь тоже следует быть внимательным, если используется своя тема для списка.
Последнее замечание относится к опытным программистам. Неправильный контекст может послужить источником утечки памяти. Если вы создадите собственный класс, в котором содержится статическая переменная, обращающая к контексту активности, то система будет держать ссылку на переменную. Если активность будет закрыта, то сборщик мусора не сможет очистить память от переменной и самой неиспользуемой активности. В таких случаях лучше использовать контекст приложения через метод getApplicationContext().
ContextCompat
В библиотеки совместимости появился свой класс для контекста ContextCompat. Он может вам пригодиться, когда студия вдруг подчеркнёт метод в старом проекте и объявит его устаревшим.
context.getResources().getColor(R.color.some_color_resource_id);
Допустим, мы хотим поменять цвет текста на кнопки.
public void onClick(View view) < int color = getResources().getColor(R.color.colorPrimary); // ругается Button button = (Button) findViewById(R.id.button); button.setTextColor(color); >
Студия ругается, что нужно использовать новый вариант getColor(int, Theme). Заменим строчку.
// Теперь не ругается int color = ContextCompat.getColor(this, R.color.colorPrimary);
Если посмотреть на исходники этого варианта, то увидим, что там тоже идёт вызов нового метода. Поэтому можно сразу использовать правильный вариант, если вы пишете под Marshmallow и выше.
// Только для API 23 и выше int color = getResources().getColor(R.color.colorPrimary, getTheme());
Что такое Context?
Кто-нибудь объясните по-человечески что такое Context в Android. Имею опыт использования, но чувствую неудовлетворенность, так как четкий его смысл ускользает. UPD. Хотелось бы узнать, исходя из вашей практики, как он меняется или не меняется в ходе работы приложения, меняется ли он от активности к активности, от активности к сервису. или он разный у каждого компонента приложения, имеет разные возможности в разных ситуациях.
Отслеживать
69.8k 9 9 золотых знаков 66 66 серебряных знаков 123 123 бронзовых знака
Урок 22. Intent, Intent Filter, Context — теория
На прошлом уроке (№ 21) мы создали приложение, которое содержит два Activity. Напомню, что для создания Activity, необходимо:
— создать класс, с суперклассом android.app.Activity
— создать Activity-запись в манифесте и указать ей созданный класс в поле Name
Надеюсь прошлый урок не вызвал особых трудностей и процедура создания Activity примерно уложилась в голове. Теперь мы можем обратить внимание на код вызова Activity.
Intent intent = new Intent(this, ActivityTwo.class); startActivity(intent);
Мы использовали объект Intent. О нем можно почитать здесь, здесь и здесь. Правда инфа достаточно сложна для понимания с нуля. Я попробую своими словами объяснить.
Что такое Intent
В нашем случае Intent – это объект, в котором мы прописываем, какое Activity нам необходимо вызвать. После чего мы передаем этот Intent-объект методу startActivity, который находит соответствующее Activity и показывает его. При создании Intent мы использовали конструктор Intent (Context packageContext, Class cls) с двумя параметрами.
Первый параметр – это Context. Если помните, когда мы программно создавали View в одном из прошлых уроков, мы тоже использовали в конструкторах объект Context. Activity является подклассом Context, поэтому мы можем использовать ее – this. Вкратце, Context – это объект, который предоставляет доступ к базовым функциям приложения таким как: доступ к ресурсам, к файловой системе, вызов Activiy и т.д. Я думаю, в дальнейшем мы рассмотрим примеры, где явно увидим, зачем Context передается и как используется.
Второй параметр – имя класса. Напомню, что при создании записи Activity в манифест-файле мы указываем имя класса. И теперь если мы укажем тот же класс в Intent – то система, просмотрев манифест-файл обнаружит соответствие и покажет соответствующий Activity.
В этом можно легко убедиться. Мы удалим запись об Activity из манифест-файла и попробуем его после этого вызвать. Откройте проект из прошлого урока P0211_TwoActivity, откройте манифест-файл, вкладка Application и удалите запись об ActivityTwo с помощью кнопки Remove. Сохраните все, запустите приложение и попробуйте вызвать Activity кнопкой “Go to Activity Two”. Приложение выдаст ошибку. Если посмотреть логи, то видим следующий текст:
ERROR/AndroidRuntime(367): android.content.ActivityNotFoundException: Unable to find explicit activity class ; have you declared this activity in your AndroidManifest.xml?
(Логи — вкладка LogCat в Eclipse. Если не видно такой, то идем в меню Window -> Show View -> Other, папка Android -> LogCat )
Система говорит нам, что не нашла такого Activity класса и любезно подсказывает, что, возможно, он не прописан в манифест-файле. Снова пропишите Activity в манифест-файле, все сохраните и запускайте. Теперь должно работать.
Явный вызов
Вызов Activity с помощью такого Intent – это явный вызов. Т.е. с помощью класса мы явно указываем какое Activity хотели бы увидеть. Это обычно используется внутри одного приложения. Схематично это можно изобразить так:

Здесь мы создаем Intent, в качестве параметра передаем ему класс Class_B. Далее вызываем метод startActivity с созданным Intent в качестве параметра. Метод проверяет AndroidManifest на наличие Activity связанной с классом Class_B и если находит, то отображает. Все это в пределах одного приложения.
Неявный вызов
Существует также неявный вызов Activity. Он отличается тем, что при создании Intent мы используем не класс, а заполняем параметры action, data, category определенными значениями. Комбинация этих значений определяют цель, которую мы хотим достичь. Например: отправка письма, открытие гиперссылки, редактирование текста, просмотр картинки, звонок по определенному номеру и т.д. В свою очередь для Activity мы прописываем Intent Filter — это набор тех же параметров: action, data, category (но значения уже свои — зависят от того, что умеет делать Activity). И если параметры нашего Intent совпадают с условиями этого фильтра, то Activity вызывается. Но при этом поиск уже идет по всем Activity всех приложений в системе. Если находится несколько, то система предоставляет вам выбор, какой именно программой вы хотите воспользоваться. Схематично это можно изобразить так:

В Application_1 создается Intent, заполняются параметры action, data, category. Для удобства, получившийся набор параметров назовем Param_C. С помощью startActivity этот Intent отправляется на поиски подходящей Activity, которая сможет выполнить то, что нам нужно (т.е. то, что определено с помощью Param_C). В системе есть разные приложения, и в каждом из них несколько Activity. Для некоторых Activity определен Intent Filter (наборы Param_A, Param_B и т.д.), для некоторых нет. Метод startActivity сверяет набор параметров Intent и наборы параметров Intent Filter для каждой Activity. Если наборы совпадают (Param_C для обоих), то Activity считается подходящей.
Если в итоге нашлась только одна Activity – она и отображается. Если же нашлось несколько подходящих Activity, то пользователю выводится список, где он может сам выбрать какое приложение ему использовать.
Например, если в системе установлено несколько музыкальных плееров, и вы запускаете mp3, то система выведет вам список Activity, которые умеют играть музыку и попросит выбрать, какое из них использовать. А те Activity, которые умеют редактить текст, показывать картинки, звонить и т.п. будут проигнорированы.
Если для Activity не задан Intent Filter (Activity_24 на схеме), то Intent с параметрами ему никак не подойдет, и оно тоже будет проигнорировано.
Если проводить аналогии — можно сравнить Intent с ключом, а Intent Filter с замкОм, за которым сидит прекрасное Activity )))
Мы будем постепенно узнавать нюансы этого механизма и значения которыми заполняются параметры action, data и category в Intent и Intent Filter. Сейчас важно понять, что в случае неявного вызова одно приложение посылает Intent, а все другие сверяют его параметры со своими Activity -> Intent Filter. Intent – это базовое понятие системы Android и без него нам никуда. Оно применяется не только для Activity. Но об этом позднее.
Ну вот, хотел написать пару вводных слов, а получилось достаточно подробное объяснение со схемами и примерами ) Надеюсь, что у меня получилось донести смысл технологии Intent-ов. В дальнейшем будем практиковаться и закрепим тему.
На следующем уроке:
— Activity LifeCycle – поведение Activity при создании, вызове, закрытии
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Singleton vs Application Context в Android
Существует общая проблема, с которой сталкиваются разработчики Android-приложений: как правильно использовать глобальные объекты? Для этого есть два распространенных подхода: паттерн Singleton и использование Application Context.
Возьмем для примера ситуацию, когда требуется использовать объект базы данных в нескольких активностях приложения. Можно создать его как Singleton, либо поместить в Application Context и получать доступ к нему через этот контекст.
Singleton
Singleton — это паттерн проектирования, который гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к нему. При использовании этого подхода следует помнить о синхронизации, чтобы предотвратить возможные проблемы многопоточности.
Преимущества Singleton:
- Простота использования.
- Позволяет легко контролировать количество экземпляров класса.
Недостатки Singleton:
- Сложности с тестированием, так как Singleton создает глобальное состояние в приложении, что может привести к нежелательным побочным эффектам.
- Нарушение принципа единственной ответственности, так как класс сам заботится о своем жизненном цикле.
Application Context
Application Context — это класс, предоставляющий доступ к глобальному состоянию приложения.
Преимущества Application Context:
- Объекты, помещенные в Application Context, живут столько, сколько живет само приложение.
- Позволяет легко передавать данные между компонентами приложения.
Недостатки Application Context:
- Возможность утечки памяти, если неправильно использовать контекст.
- Необходимо явно управлять жизненным циклом объектов.
В итоге, каждый из подходов имеет свои преимущества и недостатки. Выбор между ними зависит от конкретной задачи и предпочтений разработчика.