Content Provider — это компонент Android, который предоставляет единый интерфейс для доступа к данным между приложениями. Он абстрагирует физическое хранение (SQLite, файлы, сетевые источники) и обеспечивает безопасный обмен информацией через ContentResolver. По данным Android Developer Guide, 2026, Content Provider является одним из четырёх основных компонентов Android-приложения наряду с Activity, Service и BroadcastReceiver. Его задача — сделать данные доступными другим приложениям с контролем разрешений на чтение и запись.
Главное
Content Provider — это компонент Android, который управляет доступом к централизованному хранилищу данных и предоставляет их другим приложениям через единый контрактный интерфейс. Он скрывает детали реализации хранилища: данные могут храниться в SQLite, на файловой системе, в облаке или быть результатом сетевого запроса.
Android включает встроенные Content Provider для системных данных — ContactsContract, MediaStore, CalendarContract, CallLog. Сторонние приложения также могут создавать собственные провайдеры для безопасного обмена данными. Каждый провайдер регистрируется в AndroidManifest.xml с указанием authority — уникальной строки, которая образует первую часть URI.
Content Provider работает по модели клиент-сервер. Провайдер выступает сервером, который реализует шесть обязательных методов: query, insert, update, delete, getType и onCreate. Клиент (другое приложение) обращается к провайдеру через ContentResolver, который транслирует вызовы в соответствующие методы провайдера через IPC-механизм Android.
Каждый Content Provider идентифицируется URI схемы content://. Например, content://com.example.app.provider/items. Первая часть authority (com.example.app.provider) привязывается к классу провайдера в манифесте. Путь /items указывает на таблицу, а /items/5 — на конкретную запись с ID=5.
Когда приложение вызывает ContentResolver.query(URI), Android проверяет разрешения вызывающего пакета, находит провайдер по authority и запускает его процесс, если он ещё не запущен. Провайдер выполняет запрос и возвращает Cursor — объект, который содержит результат и позволяет клиенту итерировать по записям.
Класс ContentProvider требует реализации шести абстрактных методов. Каждый метод принимает URI и возвращает результат, соответствующий типу операции. Система вызывает эти методы из любого процесса, поэтому они должны быть потокобезопасными и не блокировать выполнение на долгое время.
Метод query принимает URI, массив столбцов projection, строку selection с аргументами и порядок сортировки. Он возвращает Cursor с данными. В реализации нужно разобрать URI с помощью UriMatcher и выполнить соответствующий SQL-запрос к базе данных.
Эти методы изменяют данные в хранилище. insert получает ContentValues — пары ключ-значение — и возвращает URI новой записи. update и delete принимают selection для фильтрации записей и возвращают количество затронутых строк. После изменения данных провайдер обязан уведомить об этом через ContentResolver.notifyChange.
| Метод | Назначение | Возврат |
|---|---|---|
| query | Получение данных по URI | Cursor или null |
| insert | Добавление новой записи | URI новой записи |
| update | Обновление существующих записей | int (кол-во строк) |
| delete | Удаление записей | int (кол-во строк) |
| getType | MIME-тип для URI | String |
| onCreate | Инициализация провайдера | boolean |
ContentResolver — это единая точка доступа для работы со всеми Content Provider в системе. Клиентское приложение никогда не вызывает методы провайдера напрямую — только через ContentResolver, который Android получает из контекста. CRUD-операции в ContentResolver имеют те же имена, что и в провайдере, но принимают URI вместо прямых ссылок.
Для разбора входящих URI внутри провайдера используется UriMatcher. Он позволяет сопоставить URI с числовым кодом — например, URI content://authority/items даёт код 1, а content://authority/items/# — код 2. Это избавляет от ручного парсинга строки URI в каждом методе.
// Using ContentResolver to access contacts
val uri = ContactsContract.Contacts.CONTENT_URI
val cursor = contentResolver.query(
uri,
arrayOf(ContactsContract.Contacts.DISPLAY_NAME),
null, null, null
)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndexOrThrow(
ContactsContract.Contacts.DISPLAY_NAME
))
Log.d("Contacts", "Name: $name")
}
}
Cursor после использования нужно обязательно закрывать — в примере выше это делает use (extension-функция Kotlin). Если Cursor не закрыть, произойдёт утечка памяти, так как он держит ссылку на данные в Binder-пуле. Для UI-сценариев используйте CursorLoader или Room с LiveData/Flow.
Создание собственного Content Provider начинается с наследования от класса ContentProvider. Провайдер работает с SQLite базой через SQLiteOpenHelper и использует UriMatcher для определения типа запроса. Рассмотрим минимальную реализацию для управления списком заметок.
Провайдер регистрируется в AndroidManifest.xml внутри тега application. Атрибут authorities задаёт уникальный идентификатор, а exported определяет, могут ли другие приложения обращаться к провайдеру. Без exported=true провайдер доступен только внутри вашего приложения.
// Example Content Provider for notes
class NotesProvider : ContentProvider() {
companion object {
const val AUTHORITY = "com.example.app.notes"
const val NOTES_PATH = "notes"
const val NOTES_URI = "content://$AUTHORITY/$NOTES_PATH"
const val NOTES_ID = "content://$AUTHORITY/$NOTES_PATH/#"
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, NOTES_PATH, 1)
addURI(AUTHORITY, "$NOTES_PATH/#", 2)
}
}
override fun query(uri: Uri, projection: Array<String>?,
selection: String?, args: Array<String>?, sort: String?): Cursor? {
return when (uriMatcher.match(uri)) {
1 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, selection, args, null, null, sort)
2 -> dbHelper.readableDatabase.query(TABLE_NOTES,
projection, "_id=?", arrayOf(uri.lastPathSegment), null, null, null)
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val id = dbHelper.writableDatabase.insert(TABLE_NOTES, null, values)
context?.contentResolver?.notifyChange(uri, null)
return ContentUris.withAppendedId(uri, id)
}
override fun delete(uri: Uri, selection: String?, args: Array<String>?): Int {
val count = dbHelper.writableDatabase.delete(TABLE_NOTES, selection, args)
context?.contentResolver?.notifyChange(uri, null)
return count
}
// getType, update, onCreate omitted for brevity
}
После создания класса провайдер нужно зарегистрировать в манифесте с атрибутами android:authorities и android:exported (true, если провайдер публичный). Система создаёт экземпляр провайдера при первом обращении к нему — это происходит в UI-потоке, поэтому onCreate должен выполняться быстро.
Content Provider позволяет управлять доступом к данным на двух уровнях: разрешения на чтение и разрешения на запись. Они задаются в манифесте атрибутами android:readPermission и android:writePermission. Если клиентское приложение не имеет соответствующего разрешения, система отклонит вызов с SecurityException.
Android поддерживает временные разрешения через флаги FLAG_GRANT_READ_URI_PERMISSION и FLAG_GRANT_WRITE_URI_PERMISSION. Это полезно, когда приложение передаёт URI файла другому приложению через Intent — получатель получает доступ только к конкретному URI на ограниченное время. Система отзывает временное разрешение после завершения работы принимающего приложения.
Для системных провайдеров Android требует указания конкретных разрешений в манифесте приложения. Например, для доступа к контактам нужно READ_CONTACTS, для календаря — READ_CALENDAR. Начиная с Android 6, эти разрешения запрашиваются во время выполнения, а не при установке.
Часто задаваемые вопросы
Content Provider — это компонент Android, который предоставляет стандартный интерфейс для обмена данными между приложениями через ContentResolver. Он абстрагирует способ хранения (SQLite, файлы, сеть) и обеспечивает безопасный доступ к данным с контролем разрешений на чтение и запись.
Authority — это уникальная строка-идентификатор провайдера, которая указывается в AndroidManifest.xml. Она образует первую часть URI content://authority/path и используется системой для маршрутизации вызовов ContentResolver к нужному провайдеру. Authority должна быть уникальной среди всех приложений на устройстве.
UriMatcher сопоставляет URI с числовыми кодами. Вы добавляете шаблоны через addURI, а затем вызываете match для получения кода по входящему URI. Это позволяет в методах query, insert, update, delete определить, какая таблица или запись запрошена, и выполнить соответствующую операцию с базой данных.
Да, Cursor обязательно нужно закрывать после использования. Если Cursor не закрыть, произойдёт утечка памяти, так как он держит Binder-ссылку на данные провайдера. В Kotlin используйте функцию use для автоматического закрытия, а в Java — try-with-resources или вызов cursor.close() в finally.
Content Provider — это компонент для межприложенческого доступа к данным, в то время как SQLiteDatabase — внутреннее хранилище одного приложения. Content Provider предоставляет URI-интерфейс и контроль разрешений, а SQLiteDatabase работает напрямую с базой без механизмов безопасности на уровне ОС.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также