Найти тему

Content Provider. Что это?

Мы знаем, что каждое приложение живет в отдельном процессе. Но иногда так бывает, что мы хотим получить доступ к данным из другого приложения. Например, хотим знать все контакты из телефонной книги или какие-то данные из чужой базы. Вот последнее я как раз довольно часто использую. У нас на работе много приложений и бывает, что нужно залезть в базу другого и что-то достать. В этом нам поможет Content Provider.

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

В Android уже есть готовые Content Providers, которые управляют такими данными, как аудио, видео, изображения и личная контактная информация. С некоторыми ограничениями эти провайдеры доступны для любого приложения (тут подробнее надо в документации смотреть).

А что там по потокам?

Если вызывающий и Content Provider находятся в одном процессе, то наш провайдер будет на том потоке, на котором находится вызов. Если процессы разные, то стандартные методы (insert, update...) используют отдельный поток. Но стоит заметить, что создаются провайдеры на главном потоке во время загрузки приложения. Поэтому тут у нас не должно быть каких-то долгих операций. У меня обычно там одна операция: inject.

Еще должна сразу сказать про Content Resolver — это сущность в нашем приложении, которая принимает запросы от клиентов и отправляет их различным провайдерам, в зависимости от того, какой указан authority (про него чуть ниже). Content Resolver содержит CRUD-методы (create, read, update, delete), которые соответствуют абстрактным методам (insert, query, update, delete) в провайдере. Наш резолвер не знает имплементацию провайдера. Он просто знает, что есть список некоторых провайдеров, и знает их authority. Каждый CRUD-метод просто передает uri и провайдер дальше сам разбирается.

Из резолвера мы получаем данные в качестве курсора. (не стала углубляться в тему, потому что вопрос про content provider, но собеседующий может попросить подробнее рассказать).

Методы провайдера:

onCreate() — Вызывается, когда провайдер стартовал на главном потоке.

query() — Тут получаем запрос от клиента. Результат возвращаем в качестве курсора.

insert() — Метод помогает вставить новую запись.

delete() — удаляем существующую запись.

update() — обновляем существующую запись.

getType() — возвращает MIME-тип данных по переданной ссылке. MIME — просто стандарт, описывающий передачу различных типов данных.

Чтобы сделать запрос, нам нужно определить некую строку в форме URI. Выглядит она обычно так: <prefix>://<authority>/<data_type>/<id>

prefix Всегда такой — ://

authority — Тут мы указываем имя провайдера, например, contacts, browser и т.п. Не в системных провайдерах там может быть длинное имя в стиле ru.my_awesome_company.thebestprovider

data_type — Тут указываем нужный тип данных. Например, если нам нужно получить все контакты из Contacts провайдера, то данные тут — people, а полная ссылка будет выглядеть так: content://contacts/people

id — Определяет какую-то конкретную запись в запросе. Например, если нам нужен контакт под номером пять, то ссылка будет такой: content://contacts/people/5.

Что за Loader? (Не думаю, что спросят, но мало ли)

Существует Loader API, которое позволяет загружать данные из Content Provider или из других источников прямо во фрагмент. Они выполняются на отдельном потоке. Что удобно, что тут есть кэширование данных, чтобы не было дублирующих запросов, например, при повороте экрана. Также тут можно имплементировать некий observer, чтобы следить за изменениями. Например, CursorLoader автоматически триггерится на изменения через ContentObserver.

И сразу скажу про LoaderManager — абстрактный класс, который управляем всеми Loader в активити или фрагменте. По сути, это просто посредник между клиентом и загрузчиками. Важно, что у одного фрагмента может быть только один LoaderManager, который может управлять множеством объектов.

Что еще может быть интересно: управляем через LoaderCallbacks, которые обязательно надо имплементировать в активити или фрагменте.

Также LoaderManager связан с жизненным циклом активити, так что нам не надо как-то вручную останавливать или заново загружать данные.

Пока что мне не приходят в голову какие-то вопросы, которые еще можно задать по этой теме. Если встречу что-то на собеседованиях — дополню.

Дубль статей в телеграмме — https://t.me/android_junior

Мой твиттер в телеграмме — https://t.me/android_junior_notes