Dostawca treści zarządza dostępem do centralnego repozytorium danych. Usługodawca jest częścią aplikacji na Androida, która często ma własny interfejs danych. Dostawcy treści są jednak głównie wykorzystywani aplikacji uzyskujących dostęp do dostawcy za pomocą obiektu klienckiego dostawcy. Dostawcy wspólnie i klientów dostawcy oferują spójny, standardowy interfejs do danych, który komunikację między procesami i bezpieczny dostęp do danych.
Zazwyczaj współpracujemy z dostawcami treści w jednym z dwóch scenariuszy: kod dostępu do istniejącego dostawcy treści w innej aplikacji lub tworzenie nowego dostawcy treści w aplikacji, który umożliwia udostępnianie danych innym aplikacjom.
Ta strona obejmuje podstawy współpracy z dostawcami treści. Aby dowiedzieć się więcej na temat wdrażania dostawców treści w Twoich własnych aplikacjach, zobacz Utwórz dostawcę treści.
W tym temacie opisano następujące zagadnienia:
- Jak działają dostawcy treści
- Interfejs API, z którego korzystasz do pobierania danych od dostawcy treści.
- Interfejs API służący do wstawiania, aktualizowania i usuwania danych u dostawcy treści.
- Inne funkcje interfejsu API, które ułatwiają pracę z dostawcami.
Omówienie
Dostawca treści prezentuje dane aplikacjom zewnętrznym w formie jednej lub kilku tabel, jak tabele w relacyjnej bazie danych. Wiersz reprezentuje instancję jakiegoś typu danych zbieranych przez dostawcę, a każda kolumna w wierszu odpowiada jednemu fragmentowi danych dla danej instancji.
Dostawca treści koordynuje dostęp do warstwy przechowywania danych w aplikacji dla wiele różnych interfejsów API i komponentów. Jak pokazano na rys. 1, są to następujące elementy:
- Przyznawanie dostępu do danych aplikacji innym aplikacjom
- Wysyłanie danych do widżetu
- Zwracanie niestandardowych sugestii wyszukiwania dla Twojej aplikacji podczas wyszukiwania
platforma wykorzystująca
SearchRecentSuggestionsProvider
- Synchronizowanie danych aplikacji z serwerem przy użyciu implementacji
AbstractThreadedSyncAdapter
- Wczytywanie danych w interfejsie za pomocą interfejsu
CursorLoader
![Relacja między dostawcą treści a innymi komponentami.](https://developer.android.com/static/guide/topics/providers/images/content-provider-tech-stack.png?authuser=9&hl=pl)
Rysunek 1. Relacja między dostawcą treści a innymi komponentami.
Dostęp do dostawcy
Aby uzyskać dostęp do danych dostawcy treści, użyj
ContentResolver
obiekt w aplikacji
Context
, aby komunikować się z dostawcą jako klient.
Obiekt ContentResolver
komunikuje się z obiektem dostawcy,
instancji klasy, która implementuje ContentProvider
.
Usługodawca
odbiera żądania danych od klientów, wykonuje żądane działanie i zwraca
wyników. Ten obiekt zawiera metody, które wywołują metody o identycznych nazwach w obiekcie dostawcy,
wystąpienie jednej z konkretnych podklasy klasy ContentProvider
.
Metody ContentResolver
zapewniają podstawowe
„CRUD” (tworzenie, pobieranie, aktualizowanie i usuwanie) pamięci trwałej.
Typowym wzorcem uzyskiwania dostępu do aplikacji ContentProvider
z poziomu interfejsu użytkownika jest
CursorLoader
, aby uruchomić zapytanie asynchroniczne w tle.
Pole Activity
lub Fragment
w interfejsie wywołuje w interfejsie
CursorLoader
do zapytania, które z kolei otrzyma
ContentProvider
za pomocą: ContentResolver
.
Dzięki temu interfejs użytkownika będzie nadal dostępny dla użytkownika podczas wykonywania zapytania. Ten obejmują interakcję kilku różnych obiektów, jak na ilustracji 2.
![Interakcja między komponentem ContentProvider, innymi klasami i miejscem na dane.](https://developer.android.com/static/guide/topics/providers/images/content-provider-interaction.png?authuser=9&hl=pl)
Rysunek 2. Interakcja między usługą ContentProvider
, innymi klasami i miejscem na dane.
Uwaga: aby uzyskać dostęp do dostawcy, aplikacja zwykle musi poprosić o określenie uprawnień w pliku manifestu. Ten wzorzec programowania opisuje się szczegółowo w Uprawnienia dostawcy treści.
Jednym z wbudowanych dostawców platformy Androida jest dostawca słownika użytkownika, przechowuje niestandardowe słowa, które użytkownik chce zachować. Tabela 1 ilustruje, dane mogą wyglądać tak:
Tabela 1. Przykładowa tabela słownika użytkownika.
słowne | identyfikator aplikacji | publikowania | region | _ID |
---|---|---|---|---|
mapreduce |
użytkownik1 | 100 | pl_PL | 1 |
precompiler |
użytkownik14 | 200 | fr_fr | 2 |
applet |
użytkownik2 | 225 | fr_CA | 3 |
const |
użytkownik1 | 255 | pt_BR | 4 |
int |
użytkownik5 | 100 | pl_PL | 5 |
W tabeli 1 każdy wiersz reprezentuje wystąpienie słowa niebędącego
które można znaleźć
w słowniku standardowym. Każda kolumna reprezentuje fragment danych dla tego słowa, taki jak
i regionie, w którym został po raz pierwszy napotkany. Nagłówki kolumn to nazwy kolumn przechowywane w
z usługodawcą. Aby na przykład odwołać się do regionu wiersza, możesz użyć kolumny locale
. Dla:
tego dostawcy, kolumna _ID
służy jako kolumna klucza podstawowego,
przez dostawcę.
Aby uzyskać listę słów i ich języków od dostawcy słownika użytkownika,
Dzwonisz do: ContentResolver.query()
.
Metoda query()
wywołuje metodę
ContentProvider.query()
zdefiniowaną przez
Dostawca słownika użytkownika. Poniższe wiersze kodu pokazują
ContentResolver.query()
– połączenie:
Kotlin
// Queries the UserDictionary and returns results cursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs.toTypedArray(), // Selection criteria sortOrder // The sort order for the returned rows )
Java
// Queries the UserDictionary and returns results cursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs, // Selection criteria sortOrder); // The sort order for the returned rows
Tabela 2 pokazuje, jak argumenty do
query(Uri,projection,selection,selectionArgs,sortOrder)
pasuje do instrukcji SQL SELECT:
Tabela 2. Porównanie wartości query()
z zapytaniem SQL.
query() argument |
SELECT słowo kluczowe/parametr | Uwagi |
---|---|---|
Uri |
FROM table_name |
Uri jest mapowany na tabelę u dostawcy o nazwie table_name. |
projection |
col,col,col,... |
projection to tablica kolumn uwzględniona w każdym wierszu
– pobrano.
|
selection |
WHERE col = value |
selection określa kryteria wyboru wierszy. |
selectionArgs |
Brak dokładnego odpowiednika. Argumenty wyboru zastępują obiekty zastępcze ? w elemencie
klauzuli wyboru.
|
|
sortOrder |
ORDER BY col,col,... |
sortOrder określa kolejność, w jakiej wiersze pojawiają się w zwróconym wyniku
Cursor
|
Identyfikatory URI treści
Identyfikator URI treści to identyfikator URI identyfikujący dane u dostawcy. Identyfikatory URI treści zawierają symboliczną nazwę całego dostawcy (jego organu), a także element nazwa wskazująca tabelę – ścieżka. Gdy dzwonisz metody klienta, aby uzyskać dostęp do tabeli u dostawcy, identyfikator URI treści tabeli jest jednym z argumentów.
W poprzednich wierszach kodu stała
CONTENT_URI
zawiera identyfikator URI treści:
tabeli Words
dostawcy słownika użytkownika. ContentResolver
analizuje autorytet identyfikatora URI i używa go do rozwiązania dostawcy przez
porównując urzędy z tabelą systemową znanych dostawców.
ContentResolver
może następnie wysłać argumenty zapytania na właściwą wartość
dostawcy usług.
ContentProvider
używa części ścieżki identyfikatora URI treści, aby wybrać
tabeli. Dostawca ma zwykle ścieżkę dla każdej udostępnianej tabeli.
Pełny identyfikator URI tabeli Words
w poprzednich wierszach kodu to:
content://user_dictionary/words
- Ciąg znaków
content://
to schemat, który jest zawsze obecny i identyfikuje go jako identyfikator URI treści. - Ciąg tekstowy
user_dictionary
to uprawnienia dostawcy. - Ciąg znaków
words
to ścieżka tabeli.
Wielu dostawców umożliwia dostęp do pojedynczego wiersza tabeli dzięki dodaniu wartości identyfikatora
na końcu identyfikatora URI. Aby na przykład pobrać wiersz, w którym _ID
to
4
ze strony dostawcy słownika użytkownika, możesz użyć tego identyfikatora URI treści:
Kotlin
val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)
Java
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Wartości identyfikatorów są często używane, gdy pobierasz zbiór wierszy, a następnie chcesz zaktualizować lub usunąć lub jednym z nich.
Uwaga: klasy Uri
i Uri.Builder
zawierają wygodne metody tworzenia poprawnie sformułowanych obiektów URI na podstawie ciągów znaków.
Klasa ContentUris
zawiera wygodne metody dołączania wartości identyfikatorów do
identyfikator URI. Poprzedni fragment kodu używa withAppendedId()
, aby dodać identyfikator do identyfikatora URI treści dostawcy słownika użytkownika.
Pobieranie danych od dostawcy
Ta sekcja opisuje, jak pobrać dane od dostawcy za pomocą dostawcy słownika użytkowników .
Dla jasności: fragmenty kodu w tej sekcji
ContentResolver.query()
w wątku UI. W
ale wykonywać asynchroniczne zapytania w osobnym wątku. Dostępne opcje
użyj klasy CursorLoader
, która została opisana
bardziej szczegółowo w
Przewodnika. Pamiętaj też, że wiersze kodu są tylko fragmentami. Nie wyświetlają pełnego,
aplikacji.
Aby pobrać dane od dostawcy, wykonaj te podstawowe czynności:
- Poproś o uprawnienia do odczytu dla dostawcy.
- Określ kod, który wysyła zapytanie do dostawcy.
Prośba o uprawnienia do odczytu
Aby pobierać dane od dostawcy, aplikacja musi mieć uprawnienia do odczytu dla
dostawcy usług. Nie możesz o nie prosić w czasie działania aplikacji. Zamiast tego musisz określić,
musisz mieć te uprawnienia w pliku manifestu za pomocą
<uses-permission>
i dokładną nazwę uprawnienia zdefiniowaną przez
dostawcy usług.
Określając ten element w pliku manifestu, żądasz tego uprawnienia aplikacji. Gdy użytkownicy instalują Twoją aplikację, domyślnie przyznają Ci tę prośbę.
Aby znaleźć dokładną nazwę uprawnienia dostępu do odczytu dla używanego dostawcy jako nazwy innych uprawnień dostępu używanych przez dostawcę, poszukaj w sekcji dokumentacji.
Rola uprawnień dostępu do dostawców została szczegółowo opisana w Uprawnienia dostawcy treści.
Dostawca słownika użytkownika definiuje uprawnienie
android.permission.READ_USER_DICTIONARY
w pliku manifestu, więc
aplikacja, która chce odczytywać dane od dostawcy, musi poprosić o to uprawnienie.
Utwórz zapytanie
Następnym krokiem w procesie pobierania danych od dostawcy jest utworzenie zapytania. Ten fragment kodu definiuje pewne zmienne umożliwiające dostęp do dostawcy słownika użytkownika:
Kotlin
// A "projection" defines the columns that are returned for each row private val mProjection: Array<String> = arrayOf( UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name ) // Defines a string to contain the selection clause private var selectionClause: String? = null // Declares an array to contain selection arguments private lateinit var selectionArgs: Array<String>
Java
// A "projection" defines the columns that are returned for each row String[] mProjection = { UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name }; // Defines a string to contain the selection clause String selectionClause = null; // Initializes an array to contain selection arguments String[] selectionArgs = {""};
Z następnego fragmentu dowiesz się, jak używać
ContentResolver.query()
, korzystając ze słownika użytkownika
Dostawca. Zapytanie klienta dostawcy jest podobne do zapytania SQL i zawiera
zestaw kolumn do zwrócenia, zestaw kryteriów wyboru oraz kolejność sortowania.
Zbiór kolumn zwracanych przez zapytanie jest nazywany odwzorowaniem.
zmienna ma wartość mProjection
.
Wyrażenie określające wiersze do pobrania jest podzielone na klauzulę wyboru i
argumentów wyboru. Klauzula wyboru to połączenie wyrażeń logicznych i logicznych,
nazwy kolumn i wartości. Zmienna to mSelectionClause
. Jeśli określisz parametr
wymienny parametr ?
zamiast wartości, metoda zapytania pobiera wartość
z tablicy argumentów wyboru, która jest zmienną mSelectionArgs
.
Jeśli w następnym fragmencie użytkownik nie wpisze słowa, zostanie ustawiona klauzula wyboru
null
, a zapytanie zwróci wszystkie słowa dostawcy. Jeśli użytkownik wpisze
słowo, klauzula wyboru jest ustawiona na UserDictionary.Words.WORD + " = ?"
i
pierwszy element tablicy z argumentami wyboru jest ustawiony na słowo wpisane przez użytkownika.
Kotlin
/* * This declares a String array to contain the selection arguments. */ private lateinit var selectionArgs: Array<String> // Gets a word from the UI searchString = searchWord.text.toString() // Insert code here to check for invalid or malicious input // If the word is the empty string, gets everything selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let { selectionClause = "${UserDictionary.Words.WORD} = ?" arrayOf(it) } ?: run { selectionClause = null emptyArray<String>() } // Does a query against the table and returns a Cursor object mCursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder // The sort order for the returned rows ) // Some providers return null if an error occurs, others throw an exception when (mCursor?.count) { null -> { /* * Insert code here to handle the error. Be sure not to use the cursor! * You might want to call android.util.Log.e() to log this error. */ } 0 -> { /* * Insert code here to notify the user that the search is unsuccessful. This isn't * necessarily an error. You might want to offer the user the option to insert a new * row, or re-type the search term. */ } else -> { // Insert code here to do something with the results } }
Java
/* * This defines a one-element String array to contain the selection argument. */ String[] selectionArgs = {""}; // Gets a word from the UI searchString = searchWord.getText().toString(); // Remember to insert code here to check for invalid or malicious input // If the word is the empty string, gets everything if (TextUtils.isEmpty(searchString)) { // Setting the selection clause to null returns all words selectionClause = null; selectionArgs[0] = ""; } else { // Constructs a selection clause that matches the word that the user entered selectionClause = UserDictionary.Words.WORD + " = ?"; // Moves the user's input string to the selection arguments selectionArgs[0] = searchString; } // Does a query against the table and returns a Cursor object mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder); // The sort order for the returned rows // Some providers return null if an error occurs, others throw an exception if (null == mCursor) { /* * Insert code here to handle the error. Be sure not to use the cursor! You can * call android.util.Log.e() to log this error. * */ // If the Cursor is empty, the provider found no matches } else if (mCursor.getCount() < 1) { /* * Insert code here to notify the user that the search is unsuccessful. This isn't necessarily * an error. You can offer the user the option to insert a new row, or re-type the * search term. */ } else { // Insert code here to do something with the results }
To zapytanie jest podobne do tej instrukcji SQL:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
W tej instrukcji SQL zamiast stałych klas kontraktu używane są rzeczywiste nazwy kolumn.
Ochrona przed złośliwymi danymi wejściowymi
Jeśli dane zarządzane przez dostawcę treści znajdują się w bazie danych SQL, w tym danych zewnętrznych niezaufanych do nieprzetworzonych instrukcji SQL może prowadzić do wstrzyknięcia kodu SQL.
Rozważ następującą klauzulę wyboru:
Kotlin
// Constructs a selection clause by concatenating the user's input to the column name var selectionClause = "var = $mUserInput"
Java
// Constructs a selection clause by concatenating the user's input to the column name String selectionClause = "var = " + userInput;
Jeśli to zrobisz, umożliwisz użytkownikowi łączenie złośliwego kodu SQL z instrukcją SQL.
Użytkownik może na przykład wpisać „nothing; USUŃ TABELĘ *; dla kolumny mUserInput
, która
daje wynik klauzuli wyboru var = nothing; DROP TABLE *;
.
Ponieważ klauzula wyboru jest traktowana jako instrukcja SQL, może to spowodować usunięcie wszystkich tabel w bazowej bazie danych SQLite, chyba że dostawca jest skonfigurowany do przechwytywania Próby wstrzykiwania SQL.
Aby uniknąć tego problemu, użyj klauzuli wyboru, w której parametr ?
jest zmienny
i osobnej tablicy argumentów wyboru. Dzięki temu dane wejściowe użytkownika
jest bezpośrednio powiązana z zapytaniem, a nie interpretowana jako część instrukcji SQL.
Dane wejściowe użytkownika nie są traktowane jako SQL, więc nie mogą wstrzykiwać złośliwego kodu SQL. Zamiast użycia
konkatenacji, aby uwzględnić dane wejściowe użytkownika, użyj tej klauzuli wyboru:
Kotlin
// Constructs a selection clause with a replaceable parameter var selectionClause = "var = ?"
Java
// Constructs a selection clause with a replaceable parameter String selectionClause = "var = ?";
Skonfiguruj tablicę argumentów wyboru w ten sposób:
Kotlin
// Defines a mutable list to contain the selection arguments var selectionArgs: MutableList<String> = mutableListOf()
Java
// Defines an array to contain the selection arguments String[] selectionArgs = {""};
Umieść wartość w tablicy argumentów wyboru w ten sposób:
Kotlin
// Adds the user's input to the selection argument selectionArgs += userInput
Java
// Sets the selection argument to the user's input selectionArgs[0] = userInput;
Klauzula wyboru, w której parametr ?
jest wymiennym parametrem i tablica
tablica z argumentami wyboru to preferowany sposób określania wyboru, nawet jeśli dostawca
w oparciu o bazę danych SQL.
Wyświetl wyniki zapytania
Metoda klienta ContentResolver.query()
zawsze
zwraca element Cursor
zawierający kolumny określone przez funkcję zapytania
prognozy dla wierszy spełniających kryteria wyboru zapytania. O
Obiekt Cursor
zapewnia losowy dostęp z możliwością odczytu do wierszy i kolumn
zawiera.
Korzystając z metod Cursor
, możesz iterować wiersze w
określać typ danych w poszczególnych kolumnach, pobierać je z kolumn i sprawdzać inne
właściwości wyników.
Niektóre implementacje Cursor
automatycznie
aktualizowanie obiektu w przypadku zmiany danych dostawcy, aktywowanie metod w obiekcie obserwatora
gdy zmieni się Cursor
lub oba.
Uwaga: dostawca może ograniczyć dostęp do kolumn na podstawie charakteru kolumny który tworzy zapytanie. Na przykład Dostawca kontaktów ogranicza dostęp do niektórych kolumn, aby adaptery synchronizacji, aby nie zwracały ich do aktywności lub usługi.
Jeśli żadne wiersze nie pasują do kryteriów wyboru, dostawca
zwraca obiekt Cursor
, dla którego
Cursor.getCount()
to
0 – czyli pusty kursor.
Jeśli wystąpi błąd wewnętrzny, wyniki zapytania zależą od konkretnego dostawcy. Może
zwróci wartość null
lub może zwrócić Exception
.
Cursor
jest listą wierszy, więc dobry sposób na wyświetlenie
zawartość elementu Cursor
to połączenie jej z elementem ListView
za pomocą SimpleCursorAdapter
.
Poniższy fragment stanowi kontynuację kodu z poprzedniego. Tworzy
SimpleCursorAdapter
obiekt zawierający obiekt Cursor
pobrane przez zapytanie i ustawia ten obiekt jako adapter dla obiektu
ListView
Kotlin
// Defines a list of columns to retrieve from the Cursor and load into an output row val wordListColumns : Array<String> = arrayOf( UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name ) // Defines a list of View IDs that receive the Cursor columns for each row val wordListItems = intArrayOf(R.id.dictWord, R.id.locale) // Creates a new SimpleCursorAdapter cursorAdapter = SimpleCursorAdapter( applicationContext, // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0 // Flags (usually none are needed) ) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter)
Java
// Defines a list of columns to retrieve from the Cursor and load into an output row String[] wordListColumns = { UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name }; // Defines a list of View IDs that receive the Cursor columns for each row int[] wordListItems = { R.id.dictWord, R.id.locale}; // Creates a new SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter( getApplicationContext(), // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0); // Flags (usually none are needed) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter);
Uwaga: aby wesprzeć ListView
z
Cursor
, kursor musi zawierać kolumnę o nazwie _ID
.
Z tego powodu wyświetlone wcześniej zapytanie pobiera kolumnę _ID
o wartości
Words
, mimo że ListView
jej nie wyświetla.
To ograniczenie wyjaśnia też, dlaczego większość dostawców ma kolumnę _ID
dla każdego z
ich tabel.
Pobieranie danych z wyników zapytania
Oprócz wyświetlania wyników zapytania możesz ich używać do innych zadań. Dla:
możesz na przykład pobrać pisownię od dostawcy słownika użytkownika, a następnie wyszukać je
z innymi usługami. Aby to zrobić, powtarzaj kolejne wiersze w tabeli Cursor
, jak pokazano w tym przykładzie:
Kotlin
/* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ mCursor?.apply { // Determine the column index of the column named "word" val index: Int = getColumnIndex(UserDictionary.Words.WORD) /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (moveToNext()) { // Gets the value from the column newWord = getString(index) // Insert code here to process the retrieved word ... // End of while loop } }
Java
// Determine the column index of the column named "word" int index = mCursor.getColumnIndex(UserDictionary.Words.WORD); /* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ if (mCursor != null) { /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (mCursor.moveToNext()) { // Gets the value from the column newWord = mCursor.getString(index); // Insert code here to process the retrieved word ... // End of while loop } } else { // Insert code here to report an error if the cursor is null or the provider threw an exception }
Implementacje typu Cursor
zawierają kilka instrukcji „get” dla
pobierania różnych typów danych z obiektu. Na przykład poprzedni fragment kodu
używa getString()
. Mają też
Metoda getType()
, która zwraca wartość wskazującą
typ danych kolumny.
Uprawnienia dostawcy treści
Aplikacja dostawcy może określać uprawnienia, które muszą mieć inne aplikacje uzyskać dostęp do danych dostawcy. Dzięki tym uprawnieniom użytkownik może dowiedzieć się, jakie dane aplikacja próbuje uzyskać dostęp. W zależności od wymagań dostawcy inne aplikacje poprosić o odpowiednie uprawnienia w celu uzyskania dostępu do dostawcy. Użytkownicy widzą żądane uprawnień podczas instalowania aplikacji.
Jeśli aplikacja dostawcy nie określa żadnych uprawnień, to inne aplikacje nie mają dostępu do danych dostawcy, chyba że dostawca zostanie wyeksportowany. Dodatkowo komponenty w aplikacji dostawcy zawsze mają pełne uprawnienia do odczytu i zapisu, niezależnie od określonych uprawnień.
Dostawca słownika użytkownika wymaga parametru
android.permission.READ_USER_DICTIONARY
, aby pobrać z niego dane.
Dostawca ma oddzielne konto android.permission.WRITE_USER_DICTIONARY
uprawnień do wstawiania, aktualizowania lub usuwania danych.
Aby uzyskać uprawnienia dostępu do dostawcy, aplikacja prosi go o
<uses-permission>
w swoim pliku manifestu. Gdy Menedżer pakietów Androida zainstaluje aplikację, użytkownik
musi zatwierdzić wszystkie uprawnienia, o które prosi aplikacja. Jeśli użytkownik je zatwierdzi,
Menedżer pakietów kontynuuje instalację. Jeśli użytkownik ich nie zatwierdzi, Menedżer pakietów
spowoduje zatrzymanie instalacji.
Następująca próbka
<uses-permission>
element żąda uprawnień z możliwością odczytu do dostawcy słownika użytkownika:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Wpływ uprawnień na dostęp dostawcy został szczegółowo wyjaśniony w Wskazówki dotyczące bezpieczeństwa
Wstawianie, aktualizowanie i usuwanie danych
W ten sam sposób, w jaki pobierasz dane od dostawcy, korzystasz też z interakcji między
klienta dostawcy i ContentProvider
dostawcy w celu modyfikowania danych.
Wywołujesz metodę ContentResolver
za pomocą argumentów przekazywanych do
odpowiednią metodę ContentProvider
. Usługodawca i dostawca
automatycznie obsługuje zabezpieczenia i komunikację między procesami.
Wstaw dane
Aby wstawić dane do dostawcy, wywołaj funkcję
ContentResolver.insert()
. Ta metoda wstawia nowy wiersz do dostawcy i zwraca dla niego identyfikator URI treści.
Ten fragment kodu pokazuje, jak wstawić nowe słowo w polu dostawcy słownika użytkownika:
Kotlin
// Defines a new Uri object that receives the result of the insertion lateinit var newUri: Uri ... // Defines an object to contain the new values to insert val newValues = ContentValues().apply { /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ put(UserDictionary.Words.APP_ID, "example.user") put(UserDictionary.Words.LOCALE, "en_US") put(UserDictionary.Words.WORD, "insert") put(UserDictionary.Words.FREQUENCY, "100") } newUri = contentResolver.insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert )
Java
// Defines a new Uri object that receives the result of the insertion Uri newUri; ... // Defines an object to contain the new values to insert ContentValues newValues = new ContentValues(); /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ newValues.put(UserDictionary.Words.APP_ID, "example.user"); newValues.put(UserDictionary.Words.LOCALE, "en_US"); newValues.put(UserDictionary.Words.WORD, "insert"); newValues.put(UserDictionary.Words.FREQUENCY, "100"); newUri = getContentResolver().insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert );
Dane z nowego wiersza trafiają do pojedynczego obiektu ContentValues
, który
przypomina kursor jednowierszowy. Kolumny w tym obiekcie nie muszą zawierać
tego samego typu danych, a jeśli w ogóle nie chcesz określać wartości,
do: null
za pomocą: ContentValues.putNull()
.
Poprzedni fragment kodu nie dodaje kolumny _ID
, ponieważ ta kolumna jest zachowywana
automatycznie. Dostawca przypisuje unikalną wartość _ID
do każdego wiersza, który
– dodano. Dostawcy zwykle używają tej wartości jako klucza podstawowego tabeli.
Identyfikator URI treści zwrócony w zasadzie newUri
identyfikuje nowo dodany wiersz z oznaczeniem
w tym formacie:
content://user_dictionary/words/<id_value>
<id_value>
to zawartość nowego wiersza: _ID
.
Większość dostawców może automatycznie wykryć tę formę identyfikatora URI treści, a następnie wykonać żądane
operacji na tym konkretnym wierszu.
Aby uzyskać wartość _ID
ze zwróconych danych Uri
, wywołaj
ContentUris.parseId()
Zaktualizuj dane
Aby zaktualizować wiersz, użyj obiektu ContentValues
ze zaktualizowanym
tak samo jak w przypadku kryteriów wstawiania i wyboru, tak jak w przypadku zapytania.
Metoda klienta, której używasz, to
ContentResolver.update()
Wystarczy dodać
do obiektu ContentValues
dla kolumn, które aktualizujesz. Jeśli
Jeśli chcesz wyczyścić zawartość kolumny, ustaw wartość null
.
Ten fragment kodu zmienia wszystkie wiersze, których język to "en"
, na
ma ustawiony język null
. Zwracana wartość to liczba zaktualizowanych wierszy.
Kotlin
// Defines an object to contain the updated values val updateValues = ContentValues().apply { /* * Sets the updated value and updates the selected words. */ putNull(UserDictionary.Words.LOCALE) } // Defines selection criteria for the rows you want to update val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?" val selectionArgs: Array<String> = arrayOf("en_%") // Defines a variable to contain the number of updated rows var rowsUpdated: Int = 0 ... rowsUpdated = contentResolver.update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines an object to contain the updated values ContentValues updateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String selectionClause = UserDictionary.Words.LOCALE + " LIKE ?"; String[] selectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int rowsUpdated = 0; ... /* * Sets the updated value and updates the selected words. */ updateValues.putNull(UserDictionary.Words.LOCALE); rowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to );
Napraw dane wejściowe użytkownika podczas rozmowy
ContentResolver.update()
Aby dowiedzieć się więcej o:
przeczytaj sekcję Ochrona przed złośliwymi danymi.
Usuń dane
Usuwanie wierszy przypomina pobieranie ich danych. Określasz kryteria wyboru wierszy
do usunięcia, a metoda klienta zwróci liczbę usuniętych wierszy.
Ten fragment kodu usuwa wiersze, których identyfikator aplikacji pasuje do "user"
. Metoda zwraca wartość
liczbę usuniętych wierszy.
Kotlin
// Defines selection criteria for the rows you want to delete val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?" val selectionArgs: Array<String> = arrayOf("user") // Defines a variable to contain the number of rows deleted var rowsDeleted: Int = 0 ... // Deletes the words that match the selection criteria rowsDeleted = contentResolver.delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines selection criteria for the rows you want to delete String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] selectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int rowsDeleted = 0; ... // Deletes the words that match the selection criteria rowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to );
Napraw dane wejściowe użytkownika podczas rozmowy
ContentResolver.delete()
Aby dowiedzieć się więcej o:
przeczytaj sekcję Ochrona przed złośliwymi danymi wejściowymi.
Typy danych dostawcy
Dostawcy treści mogą oferować wiele różnych typów danych. Dostawca słownika użytkownika ma tylko tekstu, ale dostawcy mogą też oferować te formaty:
- Liczba całkowita
- długa liczba całkowita (długi)
- liczba zmiennoprzecinkowa
- długa zmiennoprzecinkowa (liczba zmiennoprzecinkowa)
Innym często używanym przez dostawców typem danych jest duży obiekt binarny (BLOB) zaimplementowany jako
Tablica bajtów o rozmiarze 64 KB. Dostępne typy danych znajdziesz w
Cursor
klasa „get” .
Typ danych dla każdej kolumny dostawcy jest zwykle wymieniony w jego dokumentacji.
Typy danych dostawcy słownika użytkownika zostały wymienione w dokumentacji referencyjnej
dla swojej klasy umowy: UserDictionary.Words
. Klasy kontraktu to
opisane w sekcji Klasy umów.
Typ danych możesz też określić, wywołując funkcję Cursor.getType()
.
Dostawcy obsługują też informacje o typie danych MIME dla każdego zdefiniowanego przez siebie identyfikatora URI treści. Dostępne opcje korzysta z informacji o typie MIME, aby sprawdzić, czy aplikacja może obsłużyć dane lub wybrać typ obsługi na podstawie typu MIME. Zwykle potrzebujesz Typ MIME w przypadku współpracy z dostawcą, który zawiera złożone struktury danych lub pliki.
Na przykład ContactsContract.Data
narzędzia Dostawca kontaktów używa typów MIME do oznaczania typów danych kontaktów przechowywanych w każdym
. Aby uzyskać typ MIME odpowiadający identyfikatorowi URI treści, wywołaj
ContentResolver.getType()
Sekcja Informacje o typach MIME zawiera opis zarówno standardowych, jak i niestandardowych typów MIME.
Alternatywne formy dostępu dostawcy
Przy tworzeniu aplikacji ważne są 3 alternatywne formy dostępu dostawcy:
-
Dostęp grupowy: możesz utworzyć grupę wywołań dostępu za pomocą metod w
w klasie
ContentProviderOperation
, a następnie zastosuj je za pomocą:ContentResolver.applyBatch()
-
Zapytania asynchroniczne: wykonaj zapytania w osobnym wątku. Dostępne opcje
użyj obiektu
CursorLoader
. Przykłady w tabeli Przewodnik po wczytywaniu jak to zrobić. - Dostęp do danych za pomocą intencji: chociaż nie można wysyłać intencji bezpośrednio do dostawcy, można wysłać do niej intencję, która jest jest zwykle najlepszym narzędziem do modyfikowania danych dostawcy.
Dostęp wsadowy i modyfikowanie za pomocą intencji zostały opisane w dalszej części tego artykułu.
Dostęp grupowy
Dostęp wsadowy do dostawcy jest przydatny przy wstawianiem dużej liczby wierszy, np. wiersze w wielu tabelach w tym samym wywołaniu metody i ogólnie do wykonywania zbioru funkcji to transakcja między granicami procesów, czyli operacja niepodzielna.
Aby uzyskać dostęp do dostawcy w trybie wsadowym,
utwórz tablicę obiektów ContentProviderOperation
, a następnie
wysłać je do dostawcy treści,
ContentResolver.applyBatch()
Musisz zdać
autorytet dostawcy treści, a nie konkretnego identyfikatora URI treści.
Dzięki temu każdy obiekt ContentProviderOperation
w tablicy będzie działać
względem innej tabeli. Wywołanie ContentResolver.applyBatch()
zwraca tablicę wyników.
Opis klasy umowy ContactsContract.RawContacts
zawiera fragment kodu, który pokazuje wsadowe wstawianie reklam.
Dostęp do danych za pomocą intencji
Intencje mogą zapewniać pośredni dostęp do dostawcy treści. Możesz zezwolić użytkownikowi na dostęp nawet jeśli aplikacja nie ma uprawnień dostępu ze względu na: pobrania intencji wynikowej z aplikacji, która ma uprawnienia, lub przez aktywację to aplikacja, która ma uprawnienia i pozwala użytkownikowi na wykonywanie w niej pracy.
Uzyskiwanie dostępu za pomocą uprawnień tymczasowych
Możesz uzyskać dostęp do danych dostawcy treści, nawet jeśli nie masz odpowiednich uprawnień przez wysyłanie intencji do aplikacji, która ma uprawnienia odbierając intencję wynikową zawierającą uprawnienia do identyfikatora URI. Są to uprawnienia dotyczące określonego identyfikatora URI treści, ważne do momentu, gdy działanie odbiera skończą pracę. Aplikacja, która ma trwałe uprawnienia, przyznaje tymczasowo przez ustawienie flagi w intencji wyniku:
-
Uprawnienia do odczytu:
FLAG_GRANT_READ_URI_PERMISSION
-
Uprawnienia do zapisu:
FLAG_GRANT_WRITE_URI_PERMISSION
Uwaga: te flagi nie zapewniają dostawcy ogólnych uprawnień do odczytu ani zapisu. którego uprawnienia są zawarte w identyfikatorze URI treści. Dostęp dotyczy tylko identyfikatora URI.
Wysyłając identyfikatory URI treści do innej aplikacji, podaj przynajmniej jeden z tych elementów flagami. Flagi zapewniają poniższe możliwości każdej aplikacji, która otrzymuje są kierowane na Androida 11 (poziom interfejsu API 30) lub nowszego:
- odczyt i zapis danych reprezentowanych przez identyfikator URI treści; w zależności od flagi w intencji.
- Przejmij pakiet widocznośćw aplikacji zawierającej dostawcę treści pasującego do Uprawnienia dla identyfikatora URI. Aplikacja wysyłająca intencję i aplikacja, która zawiera dostawcę treści, mogą to być 2 różne aplikacje.
Dostawca definiuje uprawnienia URI dla identyfikatorów URI treści w swoim manifeście za pomocą
android:grantUriPermissions
atrybutu
<provider>
, a także
<grant-uri-permission>
element potomny tagu
<provider>
. Mechanizm uprawnień do identyfikatora URI został szczegółowo opisany w
Przewodnik po uprawnieniach na Androidzie.
Możesz na przykład pobrać dane kontaktu z usługi Dostawca kontaktów, nawet jeśli
masz uprawnienie READ_CONTACTS
. Możesz zrobić
w aplikacji wysyłającej powitanie elektroniczne do kontaktu w dniu urodzin. Zamiast
prosi o READ_CONTACTS
, co daje Ci dostęp do wszystkich
kontaktów użytkownika i wszystkich jego informacji, pozwalają mu kontrolować,
kontaktów używanych przez aplikację. Aby to zrobić, wykonaj te czynności:
-
W aplikacji wyślij intencję zawierającą działanie
ACTION_PICK
i „kontakty” Typ MIMECONTENT_ITEM_TYPE
, przy użyciu metody metodastartActivityForResult()
. - Ponieważ intencja ta pasuje do filtra intencji dla „Wybór” aplikacji Osoby aktywność wyświetla się na pierwszym planie.
-
W działaniu zaznaczania użytkownik wybiera
kontakt , aby zaktualizować. W takim przypadku aktywność wyboru wywołuje
setResult(resultcode, intent)
skonfigurować zamiar odwrócenia aplikacji. Intencja zawiera identyfikator URI treści wybranego kontaktu i „dodatkowych elementów”. flagiFLAG_GRANT_READ_URI_PERMISSION
Te flagi przyznają identyfikator URI aplikacji otrzymują uprawnienia do odczytu danych kontaktu wskazywanego przez identyfikator URI treści. Aktywność wyboru wywołuje następniefinish()
, aby aby zwrócić element sterujący do aplikacji. -
Aktywność wraca na pierwszy plan, a system wywołuje metodę
onActivityResult()
. Ta metoda otrzymuje intencję wynikową utworzoną przez aktywność związaną z wyborem w aplikacji Osoby. - Za pomocą identyfikatora URI treści z intencji wyniku możesz odczytać dane kontaktu od dostawcy kontaktów, chociaż nie wysłano prośby o stałe uprawnienia do odczytu z dostawcą w pliku manifestu. Możesz uzyskać informacje o urodzinach kontaktu. lub adres e-mail, a następnie wyślij e-powitanie.
Użyj innej aplikacji
Innym sposobem zezwolenia użytkownikowi na modyfikowanie danych, do których nie masz uprawnień dostępu, jest aktywować aplikację, która ma uprawnienia, i pozwolić użytkownikowi wykonać w niej pracę.
Na przykład aplikacja Kalendarz akceptuje
intencję ACTION_INSERT
, która pozwala aktywować
interfejsu wstawiania aplikacji. Można przekazywać
elementy dodatkowe dane w tej intencji, do których aplikacja
który wykorzystuje do wstępnego wypełnienia interfejsu. Wydarzenia cykliczne mają złożoną składnię, dlatego preferowane jest
wstawienie wydarzeń do dostawcy kalendarza to aktywowanie aplikacji Kalendarz za pomocą
ACTION_INSERT
, a następnie zezwól użytkownikowi na wstawienie w niej zdarzenia.
Wyświetlanie danych za pomocą aplikacji pomocniczej
Jeśli aplikacja ma uprawnienia dostępu, możesz nadal używać
do wyświetlania danych
w innej aplikacji. Na przykład aplikacja Kalendarz akceptuje
intencja ACTION_VIEW
, która wyświetla konkretną datę lub zdarzenie.
Dzięki temu możesz wyświetlać informacje z kalendarza bez konieczności tworzenia własnego interfejsu użytkownika.
Więcej informacji o tej funkcji znajdziesz w
Omówienie dostawcy kalendarza
Aplikacja, do której wysyłasz intencję, nie musi być aplikacją
powiązane z dostawcą. Możesz na przykład pobrać kontakt z
Skontaktuj się z dostawcą, a następnie wyślij intencję ACTION_VIEW
zawierający identyfikator URI treści obrazu kontaktu w przeglądarce obrazów.
Klasy kontraktowe
Klasa umowy definiuje stałe, które ułatwiają aplikacjom korzystanie z identyfikatorów URI treści, kolumny
nazwy, działania intencji i inne cechy dostawcy treści. Klasy kontraktu nie są
automatycznie dołączane do dostawcy. Programista dostawcy musi je zdefiniować, a następnie
udostępniać je innym programistom. Wielu dostawców oprogramowania zainstalowanego na Androidzie
platforma ma odpowiednie klasy kontraktu w pakiecie android.provider
.
Na przykład dostawca słownika użytkownika ma klasę umowy
Pole UserDictionary
zawiera stałe identyfikatory URI treści i nazwy kolumn.
identyfikator URI treści w tabeli Words
jest zdefiniowany w stałej wartości
UserDictionary.Words.CONTENT_URI
Klasa UserDictionary.Words
zawiera też stałe nazwy kolumny,
wykorzystane w przykładowych fragmentach w tym przewodniku. Na przykład rzutowanie zapytania może być
zdefiniowane w następujący sposób:
Kotlin
val projection : Array<String> = arrayOf( UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE )
Java
String[] projection = { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE };
Inną klasą umowy w przypadku dostawcy kontaktów jest ContactsContract
.
Dokumentacja referencyjna dotycząca tych zajęć zawiera przykładowe fragmenty kodu. Jedną z
podklasy (ContactsContract.Intents.Insert
) to kontrakt
, która zawiera stałe dla intencji i danych intencji.
Informacje o typach MIME
Dostawcy treści mogą zwracać standardowe typy mediów MIME, ciągi niestandardowe typu MIME lub oba te typy.
Typy MIME:
type/subtype
Na przykład znany typ MIME text/html
ma typ text
i
podtyp html
. Jeśli dostawca zwraca ten typ dla identyfikatora URI, oznacza to, że
zwraca tekst zawierający tagi HTML.
Niestandardowe typy MIME, nazywane też typami MIME poszczególnych dostawców, mają więcej zespolonych wartości type i subtype. W przypadku wielu wierszy wartość typu jest zawsze taka:
vnd.android.cursor.dir
W przypadku pojedynczego wiersza wartość typu jest zawsze następująca:
vnd.android.cursor.item
Identyfikator subtype zależy od dostawcy. Wbudowani dostawcy Androida zwykle mają prosty podtyp. Jeśli na przykład aplikacja Kontakty tworzy wiersz dla numeru telefonu, ustawia w wierszu następujący typ MIME:
vnd.android.cursor.item/phone_v2
Wartość podtypu to phone_v2
.
Inni deweloperzy mogą tworzyć własne wzorce podtypów na podstawie
nazw urzędów i tabel. Załóżmy na przykład, że dostawca udostępnia rozkłady jazdy pociągów.
Uprawnienia dostawcy to com.example.trains
i zawiera on tabele
Line1, Line2 i Line3. W odpowiedzi na ten identyfikator URI treści dla wiersza 1 tabeli:
content://com.example.trains/Line1
dostawca zwraca ten typ MIME:
vnd.android.cursor.dir/vnd.example.line1
W odpowiedzi na ten identyfikator URI treści w wierszu 5 w tabeli Line2:
content://com.example.trains/Line2/5
dostawca zwraca ten typ MIME:
vnd.android.cursor.item/vnd.example.line2
Większość dostawców treści definiuje stałe klasy umowy dla używanych typów MIME.
Klasa umowy dostawcy kontaktów: ContactsContract.RawContacts
,
Na przykład definiuje stałą
CONTENT_ITEM_TYPE
dla typu MIME:
pojedynczy wiersz nieprzetworzonego kontaktu.
Identyfikatory URI treści w pojedynczych wierszach są opisane w Identyfikatory URI treści.