Content Provider è un componente Android che fornisce un'interfaccia unificata per l'accesso ai dati tra applicazioni. Astrae l'archiviazione fisica (SQLite, file, fonti di rete) e consente lo scambio sicuro di informazioni tramite ContentResolver. Secondo Android Developer Guide, 2026, Content Provider è uno dei quattro componenti principali di un'applicazione Android, insieme ad Activity, Service e BroadcastReceiver. Il suo compito è rendere i dati disponibili ad altre applicazioni con controllo dei permessi di lettura e scrittura.
Punti chiave
Content Provider è un componente Android che gestisce l'accesso a un archivio dati centralizzato e lo fornisce ad altre applicazioni tramite un'interfaccia contrattuale unificata. Nasconde i dettagli implementativi dell'archiviazione: i dati possono essere memorizzati in SQLite, nel filesystem, nel cloud o essere il risultato di una richiesta di rete.
Android include Content Provider integrati per i dati di sistema — ContactsContract, MediaStore, CalendarContract, CallLog. Le applicazioni di terze parti possono anche creare propri provider per lo scambio sicuro di dati. Ogni provider viene registrato in AndroidManifest.xml con un authority — una stringa univoca che forma la prima parte dell'URI.
Content Provider opera secondo un modello client-server. Il provider agisce come server che implementa sei metodi obbligatori: query, insert, update, delete, getType e onCreate. Il client (un'altra applicazione) accede al provider tramite ContentResolver, che traduce le chiamate nei metodi corrispondenti del provider attraverso il meccanismo IPC di Android.
Ogni Content Provider è identificato da un URI dello schema content://. Ad esempio, content://com.example.app.provider/items. La prima parte authority (com.example.app.provider) è legata alla classe del provider nel manifest. Il percorso /items punta a una tabella, mentre /items/5 punta a un record specifico con ID=5.
Quando un'applicazione chiama ContentResolver.query(URI), Android verifica i permessi del pacchetto chiamante, trova il provider tramite l'authority e avvia il suo processo se non è già in esecuzione. Il provider esegue la query e restituisce un Cursor — un oggetto che contiene il risultato e consente al client di iterare sui record.
La classe ContentProvider richiede l'implementazione di sei metodi astratti. Ogni metodo accetta un URI e restituisce un risultato corrispondente al tipo di operazione. Il sistema chiama questi metodi da qualsiasi processo, quindi devono essere thread-safe e non bloccare l'esecuzione a lungo.
Il metodo query accetta un URI, un array di colonne projection, una stringa selection con argomenti e un ordinamento. Restituisce un Cursor con i dati. Nell'implementazione, è necessario analizzare l'URI con UriMatcher ed eseguire la query SQL corrispondente nel database.
Questi metodi modificano i dati nell'archivio. insert riceve ContentValues — coppie chiave-valore — e restituisce l'URI del nuovo record. update e delete accettano una selection per filtrare i record e restituiscono il numero di righe interessate. Dopo aver modificato i dati, il provider deve notificarlo tramite ContentResolver.notifyChange.
| Metodo | Scopo | Restituisce |
|---|---|---|
| query | Ottenere dati per URI | Cursor o null |
| insert | Aggiungere un nuovo record | URI del nuovo record |
| update | Aggiornare record esistenti | int (n. righe) |
| delete | Eliminare record | int (n. righe) |
| getType | Tipo MIME per URI | String |
| onCreate | Inizializzazione del provider | boolean |
ContentResolver è un punto di accesso unico per lavorare con tutti i Content Provider nel sistema. L'applicazione client non chiama mai direttamente i metodi del provider — solo tramite ContentResolver, che Android ottiene dal contesto. Le operazioni CRUD in ContentResolver hanno gli stessi nomi che nel provider ma accettano URI invece di riferimenti diretti.
Per analizzare gli URI in entrata all'interno del provider, si usa UriMatcher. Permette di mappare un URI a un codice numerico — ad esempio, l'URI content://authority/items produce il codice 1, e content://authority/items/# produce il codice 2. Ciò elimina la necessità di analisi manuale della stringa URI in ogni metodo.
// Usare ContentResolver per accedere ai contatti
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("Contatti", "Nome: $name")
}
}
Il Cursor deve essere sempre chiuso dopo l'uso — nell'esempio sopra, la funzione use (estensione Kotlin) lo fa. Se il Cursor non viene chiuso, si verifica una perdita di memoria perché mantiene un riferimento ai dati nel pool Binder. Per scenari UI, usa CursorLoader o Room con LiveData/Flow.
La creazione di un proprio Content Provider inizia con l'estensione della classe ContentProvider. Il provider lavora con un database SQLite tramite SQLiteOpenHelper e usa UriMatcher per determinare il tipo di query. Considera un'implementazione minima per gestire un elenco di note.
Il provider viene registrato in AndroidManifest.xml all'interno del tag application. L'attributo authorities imposta un identificatore univoco, e exported determina se altre applicazioni possono accedere al provider. Senza exported=true, il provider è accessibile solo all'interno dell'applicazione.
// Esempio di Content Provider per 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 omessi per brevità
}
Dopo aver creato la classe del provider, deve essere registrata nel manifest con gli attributi android:authorities e android:exported (true se il provider è pubblico). Il sistema crea un'istanza del provider al primo accesso — ciò avviene nel thread UI, quindi onCreate deve eseguire rapidamente.
Content Provider consente di gestire l'accesso ai dati a due livelli: permessi di lettura e permessi di scrittura. Sono impostati nel manifest con gli attributi android:readPermission e android:writePermission. Se l'applicazione client non dispone del permesso corrispondente, il sistema rifiuta la chiamata con una SecurityException.
Android supporta permessi temporanei tramite i flag FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION. Ciò è utile quando un'applicazione passa un URI di file a un'altra applicazione tramite Intent — il destinatario ottiene accesso solo a quell'URI specifico per un tempo limitato. Il sistema revoca il permesso temporaneo dopo la chiusura dell'applicazione ricevente.
Per i provider di sistema, Android richiede la specifica di permessi concreti nel manifest dell'applicazione. Ad esempio, accedere ai contatti richiede READ_CONTACTS, e accedere al calendario richiede READ_CALENDAR. A partire da Android 6, questi permessi vengono richiesti in fase di esecuzione, non durante l'installazione.
Domande frequenti
Content Provider è un componente Android che fornisce un'interfaccia standard per lo scambio di dati tra applicazioni tramite ContentResolver. Astrae il metodo di archiviazione (SQLite, file, rete) e garantisce un accesso sicuro ai dati con controllo dei permessi di lettura e scrittura.
Authority è una stringa identificativa univoca del provider specificata in AndroidManifest.xml. Forma la prima parte dell'URI content://authority/path e viene utilizzata dal sistema per instradare le chiamate ContentResolver al provider corretto. L'authority deve essere univoca tra tutte le applicazioni sul dispositivo.
UriMatcher mappa gli URI a codici numerici. Aggiungi pattern tramite addURI, poi chiami match per ottenere il codice di un URI in entrata. Ciò consente ai metodi query, insert, update e delete di determinare quale tabella o record viene richiesto ed eseguire l'operazione corrispondente nel database.
Sì, il Cursor deve essere sempre chiuso dopo l'uso. Se il Cursor non viene chiuso, si verifica una perdita di memoria perché mantiene un riferimento Binder ai dati del provider. In Kotlin, usa la funzione use per la chiusura automatica, e in Java — try-with-resources o cursor.close() in un blocco finally.
Content Provider è un componente per l'accesso ai dati tra applicazioni, mentre SQLiteDatabase è un meccanismo di archiviazione interno per una singola applicazione. Content Provider fornisce un'interfaccia URI e controllo dei permessi, mentre SQLiteDatabase lavora direttamente con il database senza meccanismi di sicurezza a livello di sistema operativo.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche