Content Provider — este o componentă Android care oferă o interfață unificată pentru accesul la date între aplicații. Abstrage stocarea fizică (SQLite, fișiere, surse de rețea) și asigură schimbul securizat de informații prin ContentResolver. Conform Android Developer Guide, 2026, Content Provider este una dintre cele patru componente principale ale unei aplicații Android, alături de Activity, Service și BroadcastReceiver. Sarcina sa este de a face datele disponibile altor aplicații cu controlul permisiunilor de citire și scriere.
Principalele puncte
Content Provider — este o componentă Android care gestionează accesul la un depozit centralizat de date și le oferă altor aplicații printr-o interfață contractuală unificată. Ascunde detaliile de implementare a stocării: datele pot fi stocate în SQLite, pe sistemul de fișiere, în nor sau pot fi rezultatul unei cereri de rețea.
Android include Content Provider încorporați pentru date de sistem — ContactsContract, MediaStore, CalendarContract, CallLog. Aplicațiile terțe pot, de asemenea, să își creeze proprii furnizori pentru schimbul securizat de date. Fiecare furnizor este înregistrat în AndroidManifest.xml cu specificarea authority — un șir unic care formează prima parte a URI.
Content Provider funcționează după modelul client-server. Furnizorul acționează ca un server care implementează șase metode obligatorii: query, insert, update, delete, getType și onCreate. Clientul (o altă aplicație) accesează furnizorul prin ContentResolver, care transmite apelurile către metodele corespunzătoare ale furnizorului prin mecanismul IPC al Android.
Fiecare Content Provider este identificat printr-un URI al schemei content://. De exemplu, content://com.example.app.provider/items. Prima parte authority (com.example.app.provider) este legată de clasa furnizorului în manifest. Calea /items indică tabela, iar /items/5 — o înregistrare specifică cu ID=5.
Când o aplicație apelează ContentResolver.query(URI), Android verifică permisiunile pachetului apelant, găsește furnizorul după authority și pornește procesul acestuia, dacă nu este deja pornit. Furnizorul execută interogarea și returnează un Cursor — un obiect care conține rezultatul și permite clientului să itereze prin înregistrări.
Clasa ContentProvider necesită implementarea a șase metode abstracte. Fiecare metodă primește un URI și returnează un rezultat corespunzător tipului de operație. Sistemul apelează aceste metode din orice proces, deci ele trebuie să fie thread-safe și să nu blocheze execuția pentru mult timp.
Metoda query primește URI, un tablou de coloane projection, un șir selection cu argumente și ordinea de sortare. Returnează un Cursor cu date. În implementare trebuie să parseze URI cu ajutorul UriMatcher și să execute interogarea SQL corespunzătoare în baza de date.
Aceste metode modifică datele în depozit. insert primește ContentValues — perechi cheie-valoare — și returnează URI-ul noii înregistrări. update și delete primesc selection pentru filtrarea înregistrărilor și returnează numărul de rânduri afectate. După modificarea datelor, furnizorul trebuie să notifice prin ContentResolver.notifyChange.
| Metodă | Scop | Returnează |
|---|---|---|
| query | Obținerea datelor după URI | Cursor sau null |
| insert | Adăugarea unei noi înregistrări | URI-ul noii înregistrări |
| update | Actualizarea înregistrărilor existente | int (număr de rânduri) |
| delete | Ștergerea înregistrărilor | int (număr de rânduri) |
| getType | Tipul MIME pentru URI | String |
| onCreate | Inițializarea furnizorului | boolean |
ContentResolver — este un punct unic de acces pentru lucrul cu toți Content Provider din sistem. Aplicația client nu apelează niciodată metodele furnizorului direct — doar prin ContentResolver, pe care Android îl obține din context. Operațiile CRUD în ContentResolver au aceleași nume ca în furnizor, dar primesc URI în loc de referințe directe.
Pentru parsarea URI-urilor primite în interiorul furnizorului se utilizează UriMatcher. Acesta permite asocierea URI cu un cod numeric — de exemplu, URI content://authority/items dă codul 1, iar content://authority/items/# — codul 2. Acest lucru elimină necesitatea parsării manuale a șirului URI în fiecare metodă.
// Utilizarea ContentResolver pentru accesarea contactelor
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("Contacte", "Nume: $name")
}
}
Cursorul trebuie închis după utilizare — în exemplul de mai sus, use (funcția de extensie Kotlin) face acest lucru. Dacă Cursor nu este închis, va apărea o scurgere de memorie, deoarece menține o referință la date în pool-ul Binder. Pentru scenariile UI, utilizați CursorLoader sau Room cu LiveData/Flow.
Crearea propriului Content Provider începe cu moștenirea clasei ContentProvider. Furnizorul lucrează cu baza de date SQLite prin SQLiteOpenHelper și utilizează UriMatcher pentru a determina tipul de interogare. Să analizăm o implementare minimală pentru gestionarea listei de note.
Furnizorul se înregistrează în AndroidManifest.xml în interiorul tagului application. Atributul authorities stabilește un identificator unic, iar exported determină dacă alte aplicații pot accesa furnizorul. Fără exported=true, furnizorul este accesibil doar în interiorul aplicației dvs.
// Exemplu de Content Provider pentru note
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 omise pentru concizie
}
După crearea clasei, furnizorul trebuie înregistrat în manifest cu atributele android:authorities și android:exported (true dacă furnizorul este public). Sistemul creează o instanță a furnizorului la prima accesare — aceasta are loc în firul UI, deci onCreate trebuie să se execute rapid.
Content Provider permite gestionarea accesului la date pe două niveluri: permisiuni de citire și permisiuni de scriere. Acestea sunt stabilite în manifest prin atributele android:readPermission și android:writePermission. Dacă aplicația client nu are permisiunea corespunzătoare, sistemul va respinge apelul cu SecurityException.
Android acceptă permisiuni temporare prin flagurile FLAG_GRANT_READ_URI_PERMISSION și FLAG_GRANT_WRITE_URI_PERMISSION. Acest lucru este util atunci când o aplicație transmite URI-ul unui fișier altei aplicații prin Intent — destinatarul primește acces doar la URI-ul specific pentru un timp limitat. Sistemul revocă permisiunea temporară după finalizarea aplicației destinatare.
Pentru furnizorii de sistem, Android necesită specificarea permisiunilor concrete în manifestul aplicației. De exemplu, pentru accesul la contacte este necesar READ_CONTACTS, pentru calendar — READ_CALENDAR. Începând cu Android 6, aceste permisiuni sunt solicitate în timpul execuției, nu la instalare.
Întrebări frecvente
Content Provider — este o componentă Android care oferă o interfață standard pentru schimbul de date între aplicații prin ContentResolver. Abstrage modul de stocare (SQLite, fișiere, rețea) și asigură acces securizat la date cu controlul permisiunilor de citire și scriere.
Authority — este un șir unic de identificare a furnizorului, specificat în AndroidManifest.xml. Acesta formează prima parte a URI content://authority/path și este utilizat de sistem pentru rutarea apelurilor ContentResolver către furnizorul corespunzător. Authority trebuie să fie unică printre toate aplicațiile de pe dispozitiv.
UriMatcher asociază URI-urile cu coduri numerice. Adăugați șabloane prin addURI, apoi apelați match pentru a obține codul pentru URI-ul primit. Acest lucru permite în metodele query, insert, update, delete să determinați ce tabelă sau înregistrare a fost solicitată și să executați operația corespunzătoare în baza de date.
Da, Cursor trebuie închis după utilizare. Dacă Cursor nu este închis, va apărea o scurgere de memorie, deoarece menține o referință Binder la datele furnizorului. În Kotlin, utilizați funcția use pentru închiderea automată, iar în Java — try-with-resources sau apelul cursor.close() în finally.
Content Provider — este o componentă pentru accesul între aplicații la date, în timp ce SQLiteDatabase este un depozit intern al unei singure aplicații. Content Provider oferă interfață URI și control al permisiunilor, iar SQLiteDatabase lucrează direct cu baza de date fără mecanisme de securitate la nivel de sistem de operare.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și