Content Provider: какво е, архитектура на компонента и принцип на работа

Автор: IT Sectr Публикувано: 2026-06-17 Време за четене: 8 мин

Content Provider — е компонент на Android, който предоставя единен интерфейс за достъп до данни между приложения. Той абстрахира физическото съхранение (SQLite, файлове, мрежови източници) и осигурява сигурен обмен на информация чрез ContentResolver. Според Android Developer Guide, 2026, Content Provider е един от четирите основни компонента на Android приложение наред с Activity, Service и BroadcastReceiver. Неговата задача е да направи данните достъпни за други приложения с контрол на разрешенията за четене и запис.

Основни точки

  • Content Provider — стандартен компонент на Android за междуприложенски обмен на данни чрез интерфейса ContentResolver.
  • URI (content://) идентифицира таблица или запис и се използва за всички CRUD операции с доставчика.
  • UriMatcher — служебен клас, който разпознава входящите URI и определя коя таблица или ред е поискан.
  • Разрешения за четене и запис се задават в манифеста и се изискват от потребителя при инсталиране.
  • CursorLoader или Room с Content Provider осигуряват асинхронно зареждане на данни от доставчика без блокиране на UI нишката.

Какво е Content Provider?

Content Provider — е компонент на Android, който управлява достъпа до централизирано хранилище за данни и ги предоставя на други приложения чрез единен договорен интерфейс. Той скрива детайлите за имплементация на съхранението: данните могат да се съхраняват в SQLite, във файловата система, в облака или да са резултат от мрежова заявка.

Android включва вградени Content Provider за системни данни — ContactsContract, MediaStore, CalendarContract, CallLog. Приложения на трети страни също могат да създават свои доставчици за сигурен обмен на данни. Всеки доставчик се регистрира в AndroidManifest.xml с посочване на authority — уникален низ, който формира първата част на URI.

Как работи Content Provider

Content Provider работи по модела клиент-сървър. Доставчикът действа като сървър, който имплементира шест задължителни метода: query, insert, update, delete, getType и onCreate. Клиентът (друго приложение) осъществява достъп до доставчика чрез ContentResolver, който предава извикванията към съответните методи на доставчика чрез IPC механизма на Android.

URI и authority

Всеки 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 — обект, който съдържа резултата и позволява на клиента да итерира през записите.

Основни методи на Content Provider

Класът ContentProvider изисква имплементация на шест абстрактни метода. Всеки метод приема URI и връща резултат, съответстващ на типа операция. Системата извиква тези методи от всеки процес, така че те трябва да бъдат thread-safe и да не блокират изпълнението за дълго време.

Метод query

Методът query приема URI, масив от колони projection, низ selection с аргументи и ред на сортиране. Връща Cursor с данни. В имплементацията трябва да се разпознае URI с помощта на UriMatcher и да се изпълни съответната SQL заявка към базата данни.

Методи insert, update, delete

Тези методи променят данните в хранилището. insert получава ContentValues — двойки ключ-стойност — и връща URI на новия запис. update и delete приемат selection за филтриране на записи и връщат брой засегнати редове. След промяна на данните, доставчикът трябва да уведоми за това чрез ContentResolver.notifyChange.

МетодПредназначениеВръща
queryПолучаване на данни по URICursor или null
insertДобавяне на нов записURI на новия запис
updateАктуализиране на съществуващи записиint (брой редове)
deleteИзтриване на записиint (брой редове)
getTypeMIME тип за URIString
onCreateИнициализация на доставчикаboolean

ContentResolver и URI в Android

ContentResolver — е единна точка за достъп за работа с всички Content Provider в системата. Клиентското приложение никога не извиква методите на доставчика директно — само чрез ContentResolver, който Android получава от контекста. CRUD операциите в ContentResolver имат същите имена като при доставчика, но приемат URI вместо директни референции.

UriMatcher

За разпознаване на входящи URI вътре в доставчика се използва UriMatcher. Той позволява съпоставяне на URI с числов код — например URI content://authority/items дава код 1, а content://authority/items/# — код 2. Това елиминира необходимостта от ръчно разпознаване на низа URI във всеки метод.

kotlin
// Използване на ContentResolver за достъп до контакти
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("Контакти", "Име: $name")
    }
}

Cursor след употреба трябва задължително да се затвори — в примера по-горе това прави use (разширяваща функция на Kotlin). Ако Cursor не бъде затворен, ще се получи изтичане на памет, тъй като той държи референция към данните в Binder пула. За UI сценарии използвайте CursorLoader или Room с LiveData/Flow.

Пример за Content Provider в Kotlin

Създаването на собствен Content Provider започва с наследяване от класа ContentProvider. Доставчикът работи с SQLite база данни чрез SQLiteOpenHelper и използва UriMatcher за определяне на типа заявка. Нека разгледаме минимална имплементация за управление на списък с бележки.

Регистрация в манифеста

Доставчикът се регистрира в AndroidManifest.xml вътре в тага application. Атрибутът authorities задава уникален идентификатор, а exported определя дали други приложения могат да осъществят достъп до доставчика. Без exported=true доставчикът е достъпен само във вашето приложение.

kotlin
// Пример за Content Provider за бележки
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 пропуснати за краткост
}

След създаване на класа, доставчикът трябва да се регистрира в манифеста с атрибутите android:authorities и android:exported (true ако доставчикът е публичен). Системата създава инстанция на доставчика при първия достъп до него — това се случва в UI нишката, така че onCreate трябва да се изпълни бързо.

Защита на данни чрез разрешения

Content Provider позволява управление на достъпа до данни на две нива: разрешения за четене и разрешения за запис. Те се задават в манифеста с атрибутите android:readPermission и android:writePermission. Ако клиентското приложение няма съответното разрешение, системата ще отхвърли извикването с SecurityException.

Разрешение на ниво URI

Android поддържа временни разрешения чрез флаговете FLAG_GRANT_READ_URI_PERMISSION и FLAG_GRANT_WRITE_URI_PERMISSION. Това е полезно, когато приложение предава URI на файл на друго приложение чрез Intent — получателят получава достъп само до конкретния URI за ограничено време. Системата отменя временното разрешение след приключване на приемащото приложение.

За системни доставчици Android изисква посочване на конкретни разрешения в манифеста на приложението. Например за достъп до контакти се изисква READ_CONTACTS, за календар — READ_CALENDAR. От Android 6 нататък тези разрешения се изискват по време на изпълнение, а не при инсталиране.

Често задавани въпроси

Какво е Content Provider в Android?

Content Provider — е компонент на Android, който предоставя стандартен интерфейс за обмен на данни между приложения чрез ContentResolver. Той абстрахира начина на съхранение (SQLite, файлове, мрежа) и осигурява сигурен достъп до данни с контрол на разрешенията за четене и запис.

Какво е authority в Content Provider?

Authority — е уникален идентификационен низ на доставчика, който се посочва в AndroidManifest.xml. Той формира първата част на URI content://authority/path и се използва от системата за насочване на ContentResolver извиквания към съответния доставчик. Authority трябва да бъде уникална сред всички приложения на устройството.

Как работи UriMatcher в Content Provider?

UriMatcher съпоставя URI с числови кодове. Добавяте шаблони чрез addURI, след което извиквате match за получаване на кода за входящ URI. Това позволява в методите query, insert, update, delete да определите коя таблица или запис е поискан и да изпълните съответната операция с базата данни.

Трябва ли да се затваря Cursor след работа с ContentResolver?

Да, Cursor трябва задължително да се затвори след употреба. Ако Cursor не бъде затворен, ще се получи изтичане на памет, тъй като той държи Binder референция към данните на доставчика. В Kotlin използвайте функцията use за автоматично затваряне, а в Java — try-with-resources или извикване на cursor.close() в finally блок.

Каква е разликата между Content Provider и SQLiteDatabase?

Content Provider — е компонент за междуприложенски достъп до данни, докато SQLiteDatabase е вътрешно хранилище на едно приложение. Content Provider предоставя URI интерфейс и контрол на разрешенията, а SQLiteDatabase работи директно с базата данни без механизми за сигурност на ниво операционна система.

Обобщение

  • Content Provider — е стандартен компонент на Android за сигурен обмен на данни между приложения чрез единен ContentResolver интерфейс.
  • URI на схемата content://authority/path/id идентифицира доставчика, таблицата и конкретния запис — всеки сегмент има строго предназначение.
  • UriMatcher опростява разпознаването на входящи URI вътре в доставчика, елиминирайки ръчното разпознаване на низове във всеки CRUD метод.
  • Доставчикът се регистрира в AndroidManifest.xml с атрибутите authorities и exported — това е системният механизъм, без който доставчикът няма да работи.
  • ContentValues се предават на insert и update като набор от двойки ключ-стойност, а Cursor се връща от query за итерация през резултатите.
  • Разрешенията readPermission и writePermission в манифеста контролират достъпа до данни, а FLAG_GRANT_URI_PERMISSION предоставя временен достъп до конкретен URI.
  • За асинхронно зареждане на данни от Content Provider използвайте CursorLoader, Room с ContentProvider или LoaderManager — това предотвратява блокирането на UI нишката.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също