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 и връща резултат, съответстващ на типа операция. Системата извиква тези методи от всеки процес, така че те трябва да бъдат thread-safe и да не блокират изпълнението за дълго време.
Методът 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 във всеки метод.
// Използване на 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 започва с наследяване от класа ContentProvider. Доставчикът работи с SQLite база данни чрез SQLiteOpenHelper и използва UriMatcher за определяне на типа заявка. Нека разгледаме минимална имплементация за управление на списък с бележки.
Доставчикът се регистрира в AndroidManifest.xml вътре в тага application. Атрибутът authorities задава уникален идентификатор, а exported определя дали други приложения могат да осъществят достъп до доставчика. Без exported=true доставчикът е достъпен само във вашето приложение.
// Пример за 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.
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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също