Dostawca treści zarządza dostępem do centralnego repozytorium danych. Wdrożenie
dostawcy jako co najmniej jednej klasy w aplikacji na Androida wraz z elementami w
plik manifestu. Jedna z Twoich klas implementuje podklasę
ContentProvider
– interfejs między Twoim dostawcą a usługą
i innych aplikacjach.
Chociaż dostawcy treści są przeznaczone do udostępniania danych innym aplikacji, możesz mieć w aplikacji działania, które pozwolą użytkownikowi zapytań i modyfikowania danych zarządzanych przez dostawcę.
Ta strona zawiera opis podstawowego procesu tworzenia dostawcy treści i listy interfejsów API do wykorzystania.
Zanim zaczniesz tworzyć
Zanim zaczniesz tworzyć dostawcę, weź pod uwagę te kwestie:
-
Zdecyduj, czy potrzebujesz dostawcy treści. Musisz tworzyć treści,
dostawcy, jeśli chcesz udostępnić co najmniej jedną z tych funkcji:
- chcesz zaoferować złożone dane lub pliki innym aplikacjom;
- Chcesz umożliwić użytkownikom kopiowanie złożonych danych z Twojej aplikacji do innych aplikacji.
- Chcesz podać niestandardowe sugestie wyszukiwania za pomocą platformy wyszukiwania.
- Chcesz udostępnić dane aplikacji widżetom.
- Chcesz zaimplementować
AbstractThreadedSyncAdapter
,CursorAdapter
lubCursorLoader
zajęcia.
Do korzystania z baz danych ani innych rodzajów danych nie potrzebujesz dostawcy. trwała pamięć masowa, jeśli użycie odbywa się w całości w ramach Twojej aplikacji. i nie potrzebujesz żadnej z wymienionych wcześniej funkcji. Zamiast tego możesz: korzystać z jednego z systemów pamięci opisanych w Informacje o miejscu na dane i pliki
- Przeczytaj podstawowe informacje o dostawcach treści, aby dowiedzieć się więcej o dostawcach i ich działaniu.
Następnie wykonaj te czynności, aby utworzyć dostawcę:
-
Projektowanie nieprzetworzonej pamięci masowej na dane. Dostawca treści udostępnia dane na 2 sposoby:
- Dane pliku
- Dane, które zwykle trafiają do plików, na przykład: zdjęcia, pliki dźwiękowe i filmy. Przechowuj pliki w prywatnym folderze aplikacji kosmosu. W odpowiedzi na żądanie pobrania pliku z innej aplikacji może zaoferować nick dla pliku.
- Uporządkowane dane
- Dane, które zwykle trafiają do bazy danych, tablicy lub podobnej struktury. Przechowuj dane w formacie zgodnym z tabelami wierszy i kolumn. wiersz A reprezentuje jednostkę, np. osobę lub przedmiot w asortymencie. Kolumna oznacza niektóre dane dotyczące podmiotu, np. imię i nazwisko osoby lub cenę produktu. Powszechnym sposobem tego typu są w bazie danych SQLite, ale można użyć dowolnego typu pamięci trwałej. Więcej informacji o typach pamięci masowej dostępnych w systemu Android, patrz Magazyn danych projektu.
-
zdefiniować konkretną implementację klasy
ContentProvider
oraz jego wymaganych metod. Ta klasa łączy Twoje dane z pozostałymi danymi System Android. Więcej informacji o tych zajęciach: Zaimplementuj sekcję klasy ContentProvider. - Zdefiniuj ciąg urzędowy dostawcy, identyfikatory URI treści i nazwy kolumn. Jeśli chcesz aplikacji dostawcy do obsługi intencji, definiowania działań intencji, dodawania danych dodatkowych i flag. Zdefiniuj także uprawnienia wymagane dla aplikacji, które mają aby uzyskać dostęp do swoich danych. Rozważ zdefiniowanie wszystkich tych wartości jako stałych w funkcji oddzielnej klasy umowy. Później możesz udostępnić tę klasę innym programistom. Więcej więcej informacji o identyfikatorach URI treści znajdziesz w Sekcja Identyfikatory URI treści projektu. Więcej informacji o intencjach znajdziesz tutaj: Intencje i dostęp do danych.
-
Dodaj inne opcjonalne elementy, takie jak przykładowe dane lub implementacja
z
AbstractThreadedSyncAdapter
, które mogą synchronizować dane między dostawcy i danych w chmurze.
Przechowywanie danych projektu
Dostawca treści to interfejs do danych zapisanych w uporządkowanym formacie. Zanim utworzysz wybrać sposób przechowywania danych. Dane można przechowywać w dowolnym a potem zaprojektować interfejs tak, aby w razie potrzeby odczytywać i zapisywać dane.
Oto niektóre technologie przechowywania danych dostępne na Androidzie:
- Jeśli pracujesz z uporządkowanymi danymi, rozważ użycie relacyjnej bazy danych takiej jak SQLite lub nierelacyjny magazyn danych klucz-wartość, taki jak LevelDB. Jeśli pracujesz z nieuporządkowanymi danymi, takimi jak pliki audio, obrazy czy filmy, jako pliki. Możesz łączyć i dopasowywać kilka różnych typów pamięci masowej oraz udostępniać je korzystając z usług jednego dostawcy treści.
-
System Android może korzystać z biblioteki trwałości sal, która
zapewnia dostęp do interfejsu API bazy danych SQLite udostępnianego przez dostawców Androida
do przechowywania danych tabel. Aby utworzyć bazę danych za pomocą tego
Library, utwórz instancję podklasy
RoomDatabase
, jak opisano w Zapisywanie danych w lokalnej bazie danych przy użyciu Pokoju.Nie musisz używać bazy danych do wdrażania repozytorium. Usługodawca wyglądają na zewnątrz jako zbiór tabel, podobnie jak w relacyjnej bazie danych, ale jest to nie jest wymagane do wewnętrznego wdrożenia dostawcy.
- Android ma różne interfejsy API do przechowywania danych w postaci plików. Aby dowiedzieć się więcej o miejscu na pliki, przeczytaj Informacje o miejscu na dane i pliki Jeśli zaprojektowanie dostawcy danych dotyczących mediów, takich jak muzyka czy filmy, wybierz dostawcę, który łączy dane w tabeli i pliki.
- W rzadkich przypadkach wdrożenie więcej niż jednego dostawcy treści może być korzystne. do jednej aplikacji. Możesz na przykład udostępnić niektóre dane widżetowi za pomocą: jednego dostawcy treści, a inny zestaw danych udostępniać aplikacji.
-
Do pracy z danymi sieciowymi używaj zajęć w funkcjach
java.net
iandroid.net
Możesz też synchronizować dane sieciowe z danymi lokalnymi np. w bazie danych, a potem oferują dane w postaci tabel lub plików.
Uwaga: jeśli w repozytorium wprowadzisz zmianę, która nie jest zgodne wstecznie, musisz oznaczyć repozytorium nową wersją numer. Musisz też zwiększyć numer wersji aplikacji, która implementuje nowego dostawcę treści. Wprowadzenie tej zmiany zapobiega zmienia wersję, powodując awarię systemu przy próbie ponownego zainstalowania która ma niezgodnego dostawcę treści.
Uwagi na temat projektowania danych
Oto kilka wskazówek dotyczących projektowania struktury danych dostawcy:
-
Dane tabeli muszą zawsze mieć „klucz podstawowy” utrzymywanej przez dostawcę kolumny.
jako unikalną wartość liczbową dla każdego wiersza. Za pomocą tej wartości możesz połączyć wiersz z powiązanymi
wierszy w innych tabelach (używając go jako „klucza obcego”). Możesz użyć dowolnej nazwy
w tej kolumnie najlepiej użyć kolumny
BaseColumns._ID
. ponieważ łączenie wyników zapytania dostawcy z polemListView
wymaga, by jedna z pobranych kolumn miała nazwę_ID
-
Jeśli chcesz udostępnić obrazy bitmap lub inne bardzo duże fragmenty danych w postaci plików,
danych w pliku, a następnie podać je pośrednio, zamiast przechowywać bezpośrednio
tabeli. Jeśli to zrobisz, musisz powiadomić użytkowników swojego dostawcy, że muszą użyć tagu
ContentResolver
, aby uzyskać dostęp do danych. -
Typ danych binarnych dużych obiektów (BLOB) umożliwia przechowywanie danych o różnych rozmiarach lub
o zróżnicowanej strukturze. W kolumnie BLOB można na przykład przechowywać
bufor protokołu lub
Struktura JSON.
Za pomocą obiektu BLOB możesz też wdrożyć tabelę niezależną od schematu. W tego typu tabeli, definiujesz kolumnę klucza podstawowego, kolumny typu MIME bardziej ogólnych kolumn jako BLOB. Znaczenie danych w kolumnach BLOB jest wskazane przez wartość w kolumnie Typ MIME. Dzięki temu możesz przechowywać różne typy wierszy w do tej samej tabeli. „Dane” dostawcy kontaktów tabela
ContactsContract.Data
to przykład interfejsu niezależnego od schematu tabeli.
Identyfikatory URI treści projektu
Identyfikator URI treści to identyfikator URI identyfikujący dane u dostawcy. Identyfikatory URI treści obejmują
symboliczną nazwę całego dostawcy (jego organu) oraz
nazwa tabeli lub pliku (ścieżka). Opcjonalna część identyfikatora wskazuje
w pojedynczym wierszu tabeli. Wszystkie metody dostępu do danych
ContentProvider
zawiera identyfikator URI treści jako argument. Dzięki temu możesz:
określić tabelę, wiersz lub plik, do których chcesz uzyskać dostęp;
Informacje o identyfikatorach URI treści znajdziesz tutaj: Podstawowe informacje o dostawcach treści
Projektowanie organów
Dostawca ma zwykle jeden urząd, który pełni funkcję jego wewnętrznej nazwy Androida. Do unikanie konfliktów z innymi dostawcami, używanie własności domeny internetowej (odwrotnie) i za podstawę działania tego urzędu. Ponieważ dotyczy to również Androida nazwy pakietów, możesz zdefiniować urząd dostawcy jako rozszerzenie nazwy z pakietu zawierającego dostawcę.
Jeśli na przykład nazwa pakietu na Androida to
com.example.<appname>
, udostępnij swojemu dostawcy
urząd: com.example.<appname>.provider
.
Projektowanie struktury ścieżki
Programiści zwykle tworzą identyfikatory URI treści na podstawie źródeł, dołączając ścieżki kierujące do
poszczególnych tabel. Jeśli np. masz 2 tabele: table1 i
table2, możesz połączyć je z autoryzacją z poprzedniego przykładu, aby uzyskać
identyfikatory URI treści
com.example.<appname>.provider/table1
i
com.example.<appname>.provider/table2
Ścieżki nie są
może być ograniczona do jednego segmentu i nie musi istnieć tabela dla każdego poziomu ścieżki.
Obsługa identyfikatorów URI treści
Zgodnie z tradycją dostawcy oferują dostęp do pojedynczego wiersza w tabeli, akceptując identyfikator URI treści
z wartością identyfikatora wiersza na końcu identyfikatora URI. Dostawcy zgodnie z konwencją dopasowują
identyfikatora do kolumny _ID
tabeli i wykonać żądany dostęp za pomocą
pasującego wiersza.
Ta konwencja ułatwia spójne projektowanie aplikacji uzyskujących dostęp do dostawcy. Aplikacja
wykonuje zapytanie względem dostawcy i wyświetla wynikowy Cursor
w ListView
przy użyciu CursorAdapter
.
Definicja obiektu CursorAdapter
wymaga jednej z kolumn w
Cursor
na _ID
Następnie wybiera w interfejsie jeden z wyświetlonych wierszy, aby zobaczyć lub zmodyfikować
i skalowalnych danych. Aplikacja uzyskuje odpowiedni wiersz z tabeli Cursor
reprezentującej
ListView
, pobiera wartość _ID
dla tego wiersza i dodaje ją do
identyfikator URI treści i wysyła żądanie dostępu do dostawcy. Dostawca może wtedy wykonać te czynności:
zapytanie lub modyfikacja do wiersza wybranego przez użytkownika.
Wzorce identyfikatora URI treści
Aby ułatwić Ci wybór czynności do wykonania w związku z identyfikatorem URI treści przychodzącej, interfejs Provider API zawiera
klasa wygodę UriMatcher
, która mapuje wzorce URI treści na
jest liczbą całkowitą. W instrukcji switch
można użyć wartości całkowitych,
wybiera żądane działanie dla identyfikatora URI treści lub identyfikatorów URI pasujących do określonego wzorca.
Wzorzec identyfikatora URI treści pasuje do identyfikatorów URI treści przy użyciu symboli wieloznacznych:
-
*
pasuje do dowolnego prawidłowego ciągu znaków o dowolnej długości. -
#
pasuje do ciągu znaków liczbowych o dowolnej długości.
Jako przykład do projektowania i kodowania obsługi identyfikatorów URI treści rozważmy dostawcę z
urząd com.example.app.provider
, który rozpoznaje następujące identyfikatory URI treści
wskazujące tabele:
-
content://com.example.app.provider/table1
: tabela o nazwietable1
. -
content://com.example.app.provider/table2/dataset1
: tabela o nazwiedataset1
-
content://com.example.app.provider/table2/dataset2
: tabela o nazwiedataset2
-
content://com.example.app.provider/table3
: tabela o nazwietable3
.
Dostawca rozpoznaje też te identyfikatory URI treści, jeśli do nich dołączany jest identyfikator wiersza, np. content://com.example.app.provider/table3/1
dla wiersza wskazywanego przez
1
w: table3
.
Oto możliwe wzorce identyfikatora URI treści:
-
content://com.example.app.provider/*
- Pasuje do dowolnego identyfikatora URI treści u dostawcy.
-
content://com.example.app.provider/table2/*
-
Pasuje do identyfikatora URI treści dla tabel
dataset1
idataset2
, ale nie pasuje do identyfikatorów URI treści dlatable1
lubtable3
. -
content://com.example.app.provider/table3/#
-
Pasuje do identyfikatora URI treści
dla pojedynczych wierszy w funkcji
table3
, jakcontent://com.example.app.provider/table3/6
w wierszu identyfikowanym przez6
Poniższy fragment kodu pokazuje, jak działają metody dostępne w pliku UriMatcher
.
Ten kod obsługuje identyfikatory URI całej tabeli inaczej niż identyfikatory URI
jednego wiersza, korzystając ze wzorca identyfikatora URI treści
content://<authority>/<path>
na tabele i
content://<authority>/<path>/<id>
dla pojedynczych wierszy.
Metoda addURI()
mapuje
i ścieżkę do wartości całkowitej. Metoda match()
zwraca wartość całkowitą dla identyfikatora URI. Instrukcja switch
wybiera pomiędzy wysyłaniem zapytań dotyczących całej tabeli a wysyłaniem zapytań dotyczących pojedynczego rekordu.
Kotlin
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path. */ addURI("com.example.app.provider", "table3", 1) /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
Java
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ uriMatcher.addURI("com.example.app.provider", "table3/#", 2); } ... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (uriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ selection = selection + "_ID = " + uri.getLastPathSegment(); break; default: ... // If the URI isn't recognized, do some error handling here } // Call the code to actually do the query }
Inna klasa, ContentUris
, zapewnia wygodne metody pracy
z częścią id
identyfikatorów URI treści. Zajęcia Uri
i
Uri.Builder
zawierają wygodne metody analizy istniejących
Uri
obiektów i tworzenie nowych.
Implementowanie klasy ContentProvider
Dostępem zarządza instancja ContentProvider
do uporządkowanego zbioru danych dzięki obsłudze żądań z innych aplikacji. Wszystkie formularze
dostępu ostatecznie wywoła parametr ContentResolver
, który następnie wywołuje konkretny
ContentProvider
.
Wymagane metody
Klasa abstrakcyjna ContentProvider
definiuje 6 metod abstrakcyjnych, które:
które wdrożysz w ramach swojego konkretnego podklasy. Wszystkie te metody oprócz
Funkcja onCreate()
jest wywoływana przez aplikację kliencką
który próbuje uzyskać dostęp do dostawcy treści.
-
query()
-
Pobierz dane od dostawcy. Użyj argumentów do wybrania tabeli, aby
w zapytaniu, wiersze i kolumny do zwrócenia, a także kolejność sortowania wyniku.
Zwraca dane jako obiekt
Cursor
. -
insert()
- Wstaw nowy wiersz w wierszu dostawcy. Użyj argumentów, aby wybrać tabeli docelowej i uzyskać wartości kolumny do użycia. Zwraca identyfikator URI treści dla nowo wstawiony wiersz.
-
update()
- Zaktualizuj istniejące wiersze u dostawcy. Użyj argumentów, aby wybrać tabelę i wiersze do zaktualizowania kolumny i pobrania zaktualizowanych wartości. Zwraca liczbę zaktualizowanych wierszy.
-
delete()
- Usuń wiersze od dostawcy. Użyj argumentów do wybrania tabeli i wierszy do usuwania. Zwraca liczbę usuniętych wierszy.
-
getType()
- Zwraca typ MIME odpowiadający identyfikatorowi URI treści. Metodę tę opisano bardziej szczegółowo szczegóły w sekcji Implementowanie typów MIME dostawcy treści.
-
onCreate()
-
Zainicjuj dostawcę. System Android wywołuje tę metodę natychmiast po niej.
tworzy dostawcę. Dostawca zostanie utworzony dopiero
ContentResolver
obiekt próbuje uzyskać do niego dostęp.
Te metody mają taki sam podpis jak metody o identycznej nazwie
ContentResolver
metody.
Podczas wdrażania tych metod musisz uwzględnić:
-
Wszystkie te metody oprócz
onCreate()
mogą być wywoływane przez wiele wątków jednocześnie, muszą więc być bezpieczne dla wątku. Aby się uczyć o wielu wątkach znajdziesz Omówienie procesów i wątków. -
Unikaj długich operacji w obszarze
onCreate()
. Odrocz zadania inicjowania do momentu, gdy będą faktycznie potrzebne. Sekcja dotycząca implementowania metody onCreate(). omawiamy tę kwestię bardziej szczegółowo. -
Mimo że musisz wdrożyć te metody, Twój kod nie musi robić nic oprócz
zwraca oczekiwany typ danych. Można na przykład uniemożliwić innym aplikacjom
do wstawiania danych do niektórych tabel przez ignorowanie wywołania funkcji
insert()
i powracanie 0.
Zaimplementuj metodę query()
Metoda ContentProvider.query()
musi zwracać obiekt Cursor
lub, jeśli
niepowodzenie, zostanie zwrócony Exception
. Jeśli używasz bazy danych SQLite jako danych
możesz zwrócić Cursor
zwrócone przez jeden z
Metody query()
klasy SQLiteDatabase
.
Jeśli zapytanie nie pasuje do żadnego wiersza, zwraca wartość Cursor
instancję, której metoda getCount()
zwraca 0.
Zwróć wartość null
tylko wtedy, gdy podczas procesu zapytania wystąpił błąd wewnętrzny.
Jeśli do przechowywania danych nie używasz bazy danych SQLite, użyj jednej z konkretnych podklas
z Cursor
. Na przykład klasa MatrixCursor
implementuje kursor, w którym każdy wiersz jest tablicą instancji Object
. Na tych zajęciach
użyj wiersza addRow()
, aby dodać nowy wiersz.
System Android musi mieć możliwość komunikacji z Exception
mogą przekraczać granice procesów. Android może to zrobić w przypadku tych przydatnych wyjątków
obsługi błędów zapytań:
-
IllegalArgumentException
. Możesz to zrobić, jeśli Twój dostawca otrzymuje nieprawidłowy identyfikator URI treści. -
NullPointerException
Wdrażanie metody insert()
Metoda insert()
dodaje element
nowy wiersz do odpowiedniej tabeli, korzystając z wartości z kolumny ContentValues
. Jeśli nazwy kolumny nie ma w argumencie ContentValues
, zostanie
możesz podać dla niego wartość domyślną w kodzie dostawcy lub w bazie danych
schemat.
Ta metoda zwraca identyfikator URI treści w nowym wierszu. Aby to zrobić, dołącz nowy element
klucza podstawowego wiersza, zwykle wartości _ID
, do identyfikatora URI treści tabeli przy użyciu operatora
withAppendedId()
Zaimplementuj metodę delete()
Metoda delete()
nie musi usuwać wierszy ze swojej pamięci. Jeśli korzystasz z adaptera synchronizacji
z dostawcą, rozważ oznaczenie usuniętego wiersza
ze słowem „delete” zamiast całkowicie usuwać wiersz. Adapter synchronizacji może
sprawdzić, czy są usunięte wiersze, i usunąć je z serwera, zanim skasujesz je od dostawcy.
Wdrażanie metody update()
Metoda update()
przyjmuje ten sam argument ContentValues
, który jest używany przez funkcję
insert()
oraz
te same argumenty selection
i selectionArgs
używane przez
delete()
i
ContentProvider.query()
Może to umożliwić ponowne użycie kodu między tymi metodami.
Wdrażanie metody onCreate()
System Android wywołuje funkcję onCreate()
po uruchomieniu dostawcy. Inicjuj tylko szybko
do wykonywania zadań w tej metodzie oraz opóźniania utworzenia bazy danych i wczytywania danych do momentu,
otrzyma żądanie tych danych. Jeśli wykonujesz długie zadania na
onCreate()
, zwolnisz
start-upu. To z kolei spowalnia odpowiedź między dostawcą
aplikacji.
Te 2 fragmenty kodu pokazują interakcję między
ContentProvider.onCreate()
i
Room.databaseBuilder()
. Pierwszy
pokazuje implementację
ContentProvider.onCreate()
, gdzie
zostanie utworzony obiekt bazy danych, a uchwyty dla obiektów dostępu do danych –
Kotlin
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Java
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Wdrażanie typów MIME komponentu ContentProvider
Klasa ContentProvider
ma 2 metody zwracania typów MIME:
-
getType()
- Jedna z wymaganych metod, które implementujesz w przypadku dowolnego dostawcy.
-
getStreamTypes()
- Metoda, którą należy wdrożyć, jeśli dostawca oferuje pliki.
Typy MIME tabel
Metoda getType()
zwraca błąd
String
w formacie MIME, który opisuje typ danych zwracanych przez treść
Argument identyfikatora URI. Argument Uri
może być wzorem, a nie określonym identyfikatorem URI.
W takim przypadku zwracany jest typ danych powiązany z identyfikatorami URI treści pasującymi do zapytania.
wzorcem.
W przypadku typowych typów danych, takich jak tekst, HTML czy JPEG,
getType()
zwraca wartość standardową
Typ MIME tych danych. Pełna lista tych standardowych typów jest dostępna w
Typy mediów MIME IANA
witryny.
W przypadku identyfikatorów URI treści wskazujących wiersz lub wiersze danych w tabeli
getType()
za możliwość zwrotu
typ MIME w specyficznym dla dostawcy formacie MIME Androida:
-
Część typu:
vnd
-
Część podtypu:
-
Jeśli wzorzec identyfikatora URI dotyczy pojedynczego wiersza:
android.cursor.item/
-
Jeśli wzorzec identyfikatora URI dotyczy więcej niż jednego wiersza:
android.cursor.dir/
-
Jeśli wzorzec identyfikatora URI dotyczy pojedynczego wiersza:
-
Część u dostawcy:
vnd.<name>
.<type>
Ty dostarczasz
<name>
i<type>
. Wartość<name>
jest unikalna globalnie, a wartość<type>
jest unikalna dla odpowiedniego identyfikatora URI wzorcem. Jeśli chodzi o<name>
, warto podać nazwę firmy lub część nazwy pakietu aplikacji na Androida. Dobry wybór dla<type>
to ciąg znaków identyfikujący tabelę powiązaną z Identyfikator URI.
Jeśli na przykład ustanowienie organu
com.example.app.provider
, aby udostępnić tabelę o nazwie
table1
, typ MIME dla wielu wierszy w tabeli table1
to:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Typ MIME pojedynczego wiersza wartości table1
to:
vnd.android.cursor.item/vnd.com.example.provider.table1
Typy MIME plików
Jeśli dostawca oferuje pliki,
getStreamTypes()
Metoda zwraca tablicę String
typów MIME dla plików dostawcy.
może zwrócić określony identyfikator URI treści. Filtruj oferowane typy MIME według typu MIME
, dzięki czemu zwracane będą tylko typy MIME, które klient chce obsługiwać.
Weźmy na przykład dostawcę, który oferuje zdjęcia w formie plików w formacie JPG,
PNG i GIF.
Jeśli aplikacja wywołuje ContentResolver.getStreamTypes()
z ciągiem filtra image/*
, dla elementu, który
jest „obrazem”,
metoda ContentProvider.getStreamTypes()
zwraca tablicę:
{ "image/jpeg", "image/png", "image/gif"}
Jeśli aplikacja potrzebuje tylko plików JPG, może zadzwonić
ContentResolver.getStreamTypes()
z ciągiem filtra *\/jpeg
,
getStreamTypes()
zwraca:
{"image/jpeg"}
Jeśli Twój dostawca nie oferuje żadnego z typów MIME wymaganych w ciągu filtra,
getStreamTypes()
zwraca null
.
Wdróż klasę umowy
Klasa umowy to klasa public final
, która zawiera stałe definicje obiektu
Identyfikatory URI, nazwy kolumn, typy MIME i inne metadane dotyczące dostawcy. Klasa
zawiera umowę między dostawcą a innymi aplikacjami, upewniając się, że dostawca
można uzyskać poprawnie nawet wtedy, gdy zmienią się rzeczywiste wartości identyfikatorów URI, nazwy kolumn,
i tak dalej.
Klasa kontraktowa pomaga również programistom, ponieważ zwykle ma mnemotechniczne nazwy stałych, więc programiści mają mniejsze szanse na używanie przez programistów nieprawidłowych wartości dla nazw kolumn i identyfikatorów URI. Jest to , może zawierać dokumentację Javadoc. Zintegrowane środowiska programistyczne, takie jak Android Studio może automatycznie uzupełniać nazwy stałe z klasy umowy i wyświetlać zasoby Javadoc dla: stałe.
Deweloperzy nie mają dostępu do pliku klasy umowy z poziomu Twojej aplikacji, ale mogą statycznie skompilować go do swojej aplikacji na podstawie dostarczonego przez Ciebie pliku JAR.
Klasa ContactsContract
i jej zagnieżdżone klasy to przykłady:
klas kontraktowych.
Wdrażanie uprawnień dostawcy treści
Uprawnienia i dostęp do wszystkich aspektów systemu Android zostały szczegółowo opisane w Wskazówki dotyczące bezpieczeństwa Zobacz też Omówienie miejsca na dane i plików opisuje zabezpieczenia i uprawnienia obowiązujące w przypadku różnych typów pamięci masowej. Oto najważniejsze kwestie:
- Domyślnie pliki danych przechowywane w pamięci wewnętrznej urządzenia są prywatne aplikacji i dostawcy.
-
SQLiteDatabase
utworzone przez Ciebie bazy danych są prywatne w aplikacji i dostawcy. - Domyślnie pliki danych zapisywane w pamięci zewnętrznej są publiczne oraz czytelne na całym świecie. Nie możesz korzystać z usług dostawcy treści, aby ograniczyć dostęp do plików w pamięci zewnętrznej, ponieważ inne aplikacje mogą używać innych wywołań interfejsu API do ich odczytu i zapisu.
- Metoda wymaga otwierania lub tworzenia plików lub baz danych SQLite w wewnętrznej pamięci urządzenia może zapewnić wszystkim innym aplikacjom dostęp zarówno do odczytu, jak i do zapisu. Jeśli użyj wewnętrznego pliku lub bazy danych jako repozytorium dostawcy i dodasz go „czytelny na całym świecie” czy „możliwe do zapisu na całym świecie” uprawnienia ustawione dla dostawcy w jego plik manifestu nie chroni Twoich danych. Domyślny dostęp do plików i baz danych w pamięć wewnętrzna jest „prywatna”, nie zmieniaj tego w repozytorium dostawcy.
Jeśli chcesz kontrolować dostęp do swoich danych za pomocą uprawnień dostawcy treści: przechowywać dane w plikach wewnętrznych, bazach danych SQLite lub w chmurze, na serwerze zdalnym oraz zachowaj prywatność plików i baz danych w aplikacji.
Wdróż uprawnienia
Domyślnie wszystkie aplikacje mogą odczytywać dane dostawcy lub do niego zapisywać, nawet jeśli dane bazowe są
prywatne, ponieważ domyślnie Twój dostawca nie ma ustawionych uprawnień. Aby to zmienić:
ustaw uprawnienia dostawcy w pliku manifestu za pomocą atrybutów lub elementów podrzędnych
elementów elementu
<provider>
. Możesz ustawić uprawnienia dotyczące całego dostawcy,
do określonych tabel, do konkretnych rekordów lub do wszystkich trzech.
Uprawnienia dla dostawcy określasz za pomocą jednego lub kilku
<permission>
elementów w pliku manifestu. Aby
unikalne dla Twojego dostawcy, użyj określania zakresu w stylu Java dla
android:name
. Możesz na przykład nazwać uprawnienie do odczytu
com.example.app.provider.permission.READ_PROVIDER
Poniższa lista opisuje zakres uprawnień dostawcy, zaczynając od uprawnień, które dotyczą całego dostawcy, a potem są coraz bardziej szczegółowe. Uprawnienia o większej szczegółowości mają pierwszeństwo przed uprawnieniami o szerszym zakresie.
- Pojedyncze uprawnienie na poziomie dostawcy do odczytu i zapisu
-
Jedno uprawnienie, które kontroluje dostęp z uprawnieniami do odczytu i zapisu do całego dostawcy, określony
z atrybutem
android:permission
atrybutu<provider>
. - Oddzielne uprawnienia na poziomie dostawcy do odczytu i zapisu
-
Uprawnienia do odczytu i zapisu dla całego dostawcy. Ty je określasz
android:readPermission
iandroid:writePermission
atrybutów<provider>
. Mają one pierwszeństwo przed uprawnieniami wymaganymi przezandroid:permission
- Uprawnienia na poziomie ścieżki
-
Uprawnienia do odczytu, zapisu i odczytu/zapisu identyfikatora URI treści u dostawcy. Ty określasz
dla każdego identyfikatora URI, który chcesz kontrolować, za pomocą
<path-permission>
element podrzędny tagu<provider>
. Dla każdego podanego identyfikatora URI treści możesz podać uprawnienia do odczytu i zapisu, do odczytu, zapisu lub wszystkie 3 opcje. Sekcja Odczyt i uprawnienia do zapisu mają pierwszeństwo przed uprawnieniami do odczytu/zapisu. Oprócz tego na poziomie ścieżki mają pierwszeństwo przed uprawnieniami na poziomie dostawcy. - Uprawnienia tymczasowe
-
Poziom uprawnień przyznający tymczasowy dostęp do aplikacji, nawet jeśli to aplikacja
nie ma uprawnień, które są zwykle wymagane. Tymczasowy
funkcja dostępu zmniejsza liczbę uprawnień, o które aplikacja prosi
i jej pliku manifestu. Gdy włączysz uprawnienia tymczasowe, jedyne aplikacje, które potrzebują tych uprawnień,
stałe uprawnienia dostępu to te, które mają stały dostęp do wszystkich
danych.
Weź pod uwagę na przykład wymagane uprawnienia, jeśli wdrażasz dostawcę poczty e-mail i aplikację, chcesz zezwolić zewnętrznej aplikacji przeglądarki obrazów na wyświetlanie załączników zdjęć z dostawcy usług. Aby przyznać przeglądarce obrazów niezbędny dostęp bez konieczności uzyskania zezwoleń, możesz ustawić tymczasowe uprawnienia dotyczące identyfikatorów URI zawartości dla zdjęć.
Zaprojektuj aplikację do poczty e-mail tak że gdy użytkownik chce wyświetlić zdjęcie, aplikacja wysyła intencję zawierającą identyfikator URI zawartości zdjęcia i flagi uprawnień do przeglądarki obrazów. Przeglądarka obrazów może i wysłać do dostawcy poczty e-mail prośbę o pobranie zdjęcia, mimo że przeglądający mają zwykłe uprawnienia do odczytu u Twojego dostawcy.
Aby włączyć uprawnienia tymczasowe, ustaw
android:grantUriPermissions
atrybutu<provider>
lub dodaj co najmniej 1 element<grant-uri-permission>
elementów podrzędnych do<provider>
. Zadzwoń do nasContext.revokeUriPermission()
za każdym razem, gdy usuniesz obsługę identyfikatora URI treści powiązanego z tymczasowym uprawnieniem z Twojego konta dostawcy usług.Wartość atrybutu określa, w jakim stopniu informacje od dostawcy są dostępne. Jeśli atrybut ma wartość
"true"
, system przyznaje tymczasowo wszystkie uprawnienia dostawcy, zastępując wszystkie wymagane uprawnienia. zgodnie z uprawnieniami na poziomie dostawcy lub ścieżki.Jeśli ta flaga jest ustawiona na
"false"
, dodaj<grant-uri-permission>
elementów podrzędnych do<provider>
. Każdy element podrzędny określa identyfikator URI treści lub Identyfikatory URI, którym przyznano dostęp tymczasowy.Aby można było przekazać aplikacji tymczasowy dostęp, intencja musi zawierać flaga
FLAG_GRANT_READ_URI_PERMISSION
,FLAG_GRANT_WRITE_URI_PERMISSION
albo obie. Te są ustawiane za pomocą metodysetFlags()
.Jeśli atrybut
android:grantUriPermissions
nie istnieje, przyjmuje się, że jest to atrybut"false"
<provider> element
Podobnie jak komponenty Activity
i Service
,
podklasa klasy ContentProvider
jest zdefiniowane w pliku manifestu aplikacji za pomocą
<provider>
. System Android pobiera następujące informacje z
element:
-
Podmiot autoryzujący
(
android:authorities
) - Nazwy symboliczne, które identyfikują całego dostawcę w systemie. Ten został opisany bardziej szczegółowo w Sekcja Identyfikatory URI treści projektu.
-
Nazwa klasy dostawcy
(
android:name
) -
Klasa, która implementuje
ContentProvider
. Te zajęcia są omówiono to szczegółowo w Zaimplementuj sekcję klasy ContentProvider. - Uprawnienia
-
Atrybuty określające uprawnienia, które inne aplikacje muszą mieć, aby uzyskać dostęp do usługi
dane dostawcy:
-
android:grantUriPermissions
: flaga uprawnień tymczasowych -
android:permission
: jedno uprawnienie do odczytu i zapisu obowiązujące w przypadku całego dostawcy -
android:readPermission
: uprawnienia do odczytu wszystkich usług -
android:writePermission
: uprawnienia do zapisu na poziomie całego dostawcy
Uprawnienia i odpowiadające im atrybuty zostały opisane w dalszej części artykułu w sekcji Implementowanie uprawnień dostawcy treści.
-
- Atrybuty uruchamiania i sterowania
-
Te atrybuty określają, jak i kiedy system Android uruchamia dostawcę,
charakterystyka procesu dostawcy i inne ustawienia środowiska wykonawczego:
-
android:enabled
: flaga umożliwiająca systemowi uruchomienie dostawcy -
android:exported
: flaga umożliwiająca innym aplikacjom korzystanie z tego dostawcy -
android:initOrder
: kolejność uruchamiania dostawcy, w porównaniu z innymi dostawcami uczestniczącymi w tym samym procesie -
android:multiProcess
: flaga umożliwiająca systemowi uruchomienie dostawcy w tym samym procesie co klient wywołujący -
android:process
: nazwa procesu, w którym działa dostawca -
android:syncable
: flaga wskazująca, że dane dostawcy mają zostać synchronizacja z danymi na serwerze
Pełna dokumentacja tych atrybutów znajduje się w przewodniku
<provider>
element. -
- Atrybuty informacyjne
-
Opcjonalna ikona i etykieta dostawcy:
-
android:icon
: rysowalny zasób zawierający ikonę dostawcy. Ikona jest wyświetlana obok etykiety dostawcy na liście aplikacji w Ustawienia > Aplikacje > Wszystkie – -
android:label
: etykieta informacyjna opisująca dostawcę, jego lub oba te problemy. Etykieta jest widoczna na liście aplikacji w Ustawienia > Aplikacje > Wszystkie –
Pełna dokumentacja tych atrybutów znajduje się w przewodniku
<provider>
element. -
Uwaga: jeśli kierujesz aplikację na Androida 11 lub nowszego, zapoznaj się z artykułem dokumentacja dotycząca widoczności pakietów w celach związanych z konfiguracją.
Intencje i dostęp do danych
Aplikacje mogą uzyskiwać dostęp do dostawcy treści pośrednio za pomocą Intent
.
Aplikacja nie wywołuje żadnej z metod obiektu ContentResolver
lub
ContentProvider
Zamiast tego wysyła intencję, która rozpoczyna działanie,
co często jest częścią aplikacji dostawcy. Aktywność w miejscu docelowym odpowiada za:
pobieranie i wyświetlanie danych w interfejsie użytkownika.
W zależności od działania intencji aktywność w miejscu docelowym może też prosić użytkownika o wprowadzenie zmian w danych dostawcy. Intencja może też zawierać słowa „extras” dane wyświetlane w ramach aktywności docelowej w interfejsie. Użytkownik może potem zmienić te dane, zanim użyje ich do zmodyfikowania u dostawcy.
Dostęp do intencji pomaga w integralności danych. To, z usług którego dostawcy korzystasz, może zależeć wstawianie, aktualizowanie i usuwanie danych zgodnie ze ściśle zdefiniowaną logiką biznesową. Jeśli w tej sytuacji, zezwolenie innym aplikacjom na bezpośrednie modyfikowanie danych może nieprawidłowych danych.
Jeśli chcesz, aby deweloperzy korzystali z dostępu do intencji, dokładnie udokumentuj to działanie. Wyjaśnij, dlaczego dostęp do intencji za pomocą interfejsu aplikacji jest lepszy niż próba modyfikować dane za pomocą kodu.
Obsługa intencji przychodzącej, która chce zmodyfikować dane dostawcy, niczym się nie różni obsługi innych intencji. Więcej informacji o używaniu intencji znajdziesz w artykule Filtry intencji i zamiarów.
Dodatkowe powiązane informacje znajdziesz tutaj: Omówienie dostawcy kalendarza