Content Provider: co to jest, architektura komponentu i zasada działania

Autor: IT Sectr Opublikowano: 2026-06-17 Czas czytania: 8 min

Content Provider — to komponent Androida, który zapewnia jednolity interfejs dostępu do danych między aplikacjami. Abstrahuje fizyczne przechowywanie (SQLite, pliki, źródła sieciowe) i zapewnia bezpieczną wymianę informacji przez ContentResolver. Według Android Developer Guide, 2026, Content Provider jest jednym z czterech podstawowych komponentów aplikacji Android obok Activity, Service i BroadcastReceiver. Jego zadaniem jest udostępnienie danych innym aplikacjom z kontrolą uprawnień odczytu i zapisu.

Najważniejsze

  • Content Provider — standardowy komponent Androida do międzyaplikacyjnej wymiany danych przez interfejs ContentResolver.
  • URI (content://) identyfikuje tabelę lub rekord i jest używane we wszystkich operacjach CRUD z dostawcą.
  • UriMatcher — klasa pomocnicza, która parsuje przychodzące URI i określa, która tabela lub wiersz jest żądany.
  • Uprawnienia odczytu i zapisu są określane w manifeście i wymagane od użytkownika podczas instalacji.
  • CursorLoader lub Room z Content Provider zapewniają asynchroniczne ładowanie danych z dostawcy bez blokowania wątku UI.

Co to jest Content Provider?

Content Provider — to komponent Androida, który zarządza dostępem do scentralizowanego magazynu danych i udostępnia je innym aplikacjom przez jednolity interfejs kontraktowy. Ukrywa szczegóły implementacji przechowywania: dane mogą być przechowywane w SQLite, systemie plików, chmurze lub być wynikiem żądania sieciowego.

Android zawiera wbudowane Content Provider dla danych systemowych — ContactsContract, MediaStore, CalendarContract, CallLog. Aplikacje innych firm również mogą tworzyć własnych dostawców do bezpiecznej wymiany danych. Każdy dostawca jest rejestrowany w AndroidManifest.xml z określeniem authority — unikalnego ciągu znaków, który stanowi pierwszą część URI.

Jak działa Content Provider

Content Provider działa według modelu klient-serwer. Dostawca działa jako serwer, który implementuje sześć obowiązkowych metod: query, insert, update, delete, getType i onCreate. Klient (inna aplikacja) uzyskuje dostęp do dostawcy przez ContentResolver, który przekazuje wywołania do odpowiednich metod dostawcy przez mechanizm IPC Androida.

URI i authority

Każdy Content Provider jest identyfikowany przez URI schematu content://. Na przykład content://com.example.app.provider/items. Pierwsza część authority (com.example.app.provider) jest powiązana z klasą dostawcy w manifeście. Ścieżka /items wskazuje tabelę, a /items/5 — konkretny rekord o ID=5.

Proces wywołania

Gdy aplikacja wywołuje ContentResolver.query(URI), Android sprawdza uprawnienia wywołującego pakietu, znajduje dostawcę po authority i uruchamia jego proces, jeśli jeszcze nie jest uruchomiony. Dostawca wykonuje zapytanie i zwraca Cursor — obiekt zawierający wynik, który pozwala klientowi iterować po rekordach.

Główne metody Content Provider

Klasa ContentProvider wymaga implementacji sześciu abstrakcyjnych metod. Każda metoda przyjmuje URI i zwraca wynik odpowiedni do typu operacji. System wywołuje te metody z dowolnego procesu, więc muszą być bezpieczne wątkowo i nie blokować wykonania na długi czas.

Metoda query

Metoda query przyjmuje URI, tablicę kolumn projection, ciąg selekcji selection z argumentami i kolejność sortowania. Zwraca Cursor z danymi. W implementacji należy rozparsować URI za pomocą UriMatcher i wykonać odpowiednie zapytanie SQL do bazy danych.

Metody insert, update, delete

Te metody modyfikują dane w magazynie. insert otrzymuje ContentValues — pary klucz-wartość — i zwraca URI nowego rekordu. update i delete przyjmują selection do filtrowania rekordów i zwracają liczbę dotkniętych wierszy. Po zmianie danych dostawca musi powiadomić o tym przez ContentResolver.notifyChange.

MetodaPrzeznaczenieZwracana wartość
queryPobranie danych po URICursor lub null
insertDodanie nowego rekorduURI nowego rekordu
updateAktualizacja istniejących rekordówint (liczba wierszy)
deleteUsunięcie rekordówint (liczba wierszy)
getTypeTyp MIME dla URIString
onCreateInicjalizacja dostawcyboolean

ContentResolver i URI w Android

ContentResolver — to jednolity punkt dostępu do pracy ze wszystkimi Content Provider w systemie. Aplikacja kliencka nigdy nie wywołuje metod dostawcy bezpośrednio — tylko przez ContentResolver, który Android uzyskuje z kontekstu. Operacje CRUD w ContentResolver mają te same nazwy co u dostawcy, ale przyjmują URI zamiast bezpośrednich referencji.

UriMatcher

Do parsowania przychodzących URI wewnątrz dostawcy używany jest UriMatcher. Pozwala on dopasować URI do kodu numerycznego — na przykład URI content://authority/items daje kod 1, a content://authority/items/# — kod 2. To eliminuje konieczność ręcznego parsowania ciągu URI w każdej metodzie.

kotlin
// Używanie ContentResolver do dostępu do kontaktów
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("Kontakty", "Nazwa: $name")
    }
}

Cursor po użyciu należy koniecznie zamknąć — w powyższym przykładzie robi to use (funkcja rozszerzająca Kotlin). Jeśli Cursor nie zostanie zamknięty, nastąpi wyciek pamięci, ponieważ utrzymuje on referencję do danych w puli Binder. Do scenariuszy UI używaj CursorLoader lub Room z LiveData/Flow.

Przykład Content Provider w Kotlin

Tworzenie własnego Content Provider zaczyna się od dziedziczenia po klasie ContentProvider. Dostawca współpracuje z bazą SQLite przez SQLiteOpenHelper i używa UriMatcher do określenia typu zapytania. Rozważmy minimalną implementację do zarządzania listą notatek.

Rejestracja w manifeście

Dostawca rejestrowany jest w AndroidManifest.xml wewnątrz tagu application. Atrybut authorities określa unikalny identyfikator, a exported decyduje, czy inne aplikacje mogą uzyskać dostęp do dostawcy. Bez exported=true dostawca jest dostępny tylko wewnątrz twojej aplikacji.

kotlin
// Przykład Content Provider dla notatek
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 pominięte dla zwięzłości
}

Po utworzeniu klasy dostawcę należy zarejestrować w manifeście z atrybutami android:authorities i android:exported (true, jeśli dostawca jest publiczny). System tworzy instancję dostawcy przy pierwszym odwołaniu do niego — dzieje się to w wątku UI, więc onCreate powinien wykonywać się szybko.

Ochrona danych przez uprawnienia

Content Provider pozwala zarządzać dostępem do danych na dwóch poziomach: uprawnienia odczytu i uprawnienia zapisu. Są one określone w manifeście atrybutami android:readPermission i android:writePermission. Jeśli aplikacja kliencka nie ma odpowiedniego uprawnienia, system odrzuci wywołanie z SecurityException.

Uprawnienia na poziomie URI

Android obsługuje tymczasowe uprawnienia przez flagi FLAG_GRANT_READ_URI_PERMISSION i FLAG_GRANT_WRITE_URI_PERMISSION. Jest to przydatne, gdy aplikacja przekazuje URI pliku innej aplikacji przez Intent — odbiorca uzyskuje dostęp tylko do konkretnego URI na ograniczony czas. System cofa tymczasowe uprawnienie po zakończeniu działania aplikacji odbierającej.

Dla systemowych dostawców Android wymaga określenia konkretnych uprawnień w manifeście aplikacji. Na przykład do dostępu do kontaktów potrzebne jest READ_CONTACTS, do kalendarza — READ_CALENDAR. Od Androida 6 uprawnienia te są wymagane podczas działania, a nie przy instalacji.

Często zadawane pytania

Co to jest Content Provider w Android?

Content Provider — to komponent Androida, który zapewnia standardowy interfejs do wymiany danych między aplikacjami przez ContentResolver. Abstrahuje sposób przechowywania (SQLite, pliki, sieć) i zapewnia bezpieczny dostęp do danych z kontrolą uprawnień odczytu i zapisu.

Co to jest authority w Content Provider?

Authority — to unikalny ciąg znaków identyfikujący dostawcę, który jest określony w AndroidManifest.xml. Stanowi pierwszą część URI content://authority/path i jest używany przez system do kierowania wywołań ContentResolver do odpowiedniego dostawcy. Authority musi być unikalna wśród wszystkich aplikacji na urządzeniu.

Jak działa UriMatcher w Content Provider?

UriMatcher dopasowuje URI do kodów numerycznych. Dodajesz wzorce przez addURI, a następnie wywołujesz match, aby uzyskać kod dla przychodzącego URI. Pozwala to w metodach query, insert, update, delete określić, która tabela lub rekord jest żądany i wykonać odpowiednią operację na bazie danych.

Czy trzeba zamykać Cursor po pracy z ContentResolver?

Tak, Cursor należy koniecznie zamknąć po użyciu. Jeśli Cursor nie zostanie zamknięty, nastąpi wyciek pamięci, ponieważ utrzymuje on referencję Binder do danych dostawcy. W Kotlin używaj funkcji use do automatycznego zamykania, a w Javie — try-with-resources lub wywołania cursor.close() w finally.

Czym różni się Content Provider od SQLiteDatabase?

Content Provider — to komponent do międzyaplikacyjnego dostępu do danych, podczas gdy SQLiteDatabase to wewnętrzne przechowywanie jednej aplikacji. Content Provider udostępnia interfejs URI i kontrolę uprawnień, a SQLiteDatabase działa bezpośrednio z bazą bez mechanizmów bezpieczeństwa na poziomie systemu operacyjnego.

Podsumowanie

  • Content Provider — to standardowy komponent Androida do bezpiecznej wymiany danych między aplikacjami przez jednolity interfejs ContentResolver.
  • URI schematu content://authority/path/id identyfikuje dostawcę, tabelę i konkretny rekord — każdy segment ma ściśle określone przeznaczenie.
  • UriMatcher upraszcza parsowanie przychodzących URI wewnątrz dostawcy, eliminując ręczne parsowanie ciągów w każdej metodzie CRUD.
  • Dostawca rejestrowany jest w AndroidManifest.xml z atrybutami authorities i exported — to mechanizm systemowy, bez którego dostawca nie będzie działać.
  • ContentValues są przekazywane do insert i update jako zestaw par klucz-wartość, a Cursor jest zwracany z query do iteracji po wynikach.
  • Uprawnienia readPermission i writePermission w manifeście kontrolują dostęp do danych, a FLAG_GRANT_URI_PERMISSION daje tymczasowy dostęp do konkretnego URI.
  • Do asynchronicznego ładowania danych z Content Provider używaj CursorLoader, Room z ContentProvider lub LoaderManager — zapobiega to blokowaniu wątku UI.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również