page.title=Основные сведения о поставщике контента @jd:body
Поставщик контента управляет доступом к центральному репозиторию данных. Поставщик является компонентом приложения Android, который зачастую имеет собственный пользовательский интерфейс для работы с данными. Однако поставщики контента предназначены в первую очередь для использования другими приложениями, которые получают доступ к поставщику посредством клиентского объекта поставщика. Вместе поставщики и клиенты поставщиков обеспечивают согласованный, стандартный интерфейс к данным, который также обрабатывает взаимодействие между процессами и обеспечивает защищенный доступ к данным.
В этой статье рассматриваются основные сведения, касающиеся следующих тем:
Поставщик контента предоставляет данные внешним приложениям в виде одной или нескольких таблиц, аналогичных таблицам в реляционной базе данных. Строка представляет собой экземпляр некоторого типа собираемых поставщиком данных, а каждый столбец в этой строке — это отдельный элемент данных, собранных для экземпляра.
Примером встроенного поставщика в платформе Android может служить пользовательский словарь, в котором хранятся данные о написании нестандартных слов, добавленных пользователем. В таблице 1 показано, как данные могут выглядеть в этой таблице поставщика.
word | app id | frequency | locale | _ID |
---|---|---|---|---|
mapreduce | user1 | 100 | en_US | 1 |
precompiler | user14 | 200 | fr_FR | 2 |
applet | user2 | 225 | fr_CA | 3 |
const | user1 | 255 | pt_BR | 4 |
int | user5 | 100 | en_UK | 5 |
В каждой строке таблицы 1 представлен экземпляр слова, которое отсутствует
в стандартном словаре. В каждом ее столбце содержатся некоторые данные для слова,
например, данные о языке, на котором это слово было впервые использовано. Заголовки столбцов представляют собой имена столбцов, которые хранятся
в поставщике. Чтобы узнать язык строки, необходимо обратиться к столбцу locale
. В этом
поставщике столбец _ID
выступает в роли «основного ключа»,
который поставщик автоматически сохраняет.
Примечание. В поставщике необязательно должен быть основной ключ, а также ему необязательно
использовать_ID
в качестве имени столбца основного ключа, если таковой имеется. Однако, если
необходимо привязать данные из поставщика к классу {@link android.widget.ListView},
один из столбцов должен именоваться _ID
. Дополнительные сведения об этом требовании представлены в разделе
Отображение результатов запроса.
Для доступа приложения к данным из поставщика контента используется клиентский объект {@link android.content.ContentResolver}. В этом объекте имеются методы, которые вызывают идентичные методы в объекте поставщика, который представляет собой экземпляр одного из конкретных подклассов класса {@link android.content.ContentProvider}. В этих методах {@link android.content.ContentResolver} представлены основные функции CRUD (аббревиатура create, retrieve, update, delete [создание, получение, обновление и удаление]) постоянного хранилища.
Объект {@link android.content.ContentResolver} в процессе клиентского приложения и объект {@link android.content.ContentProvider} в приложении, которое владеет поставщиком, автоматически обрабатывают взаимодействие между процессами. Объект {@link android.content.ContentProvider} также выступает в роли уровня абстракции между репозиторием данных и внешним представлением данных в виде таблиц.
Примечание. Для доступа к поставщику ваше приложение обычно должно запросить определенные разрешения в своем файле манифеста. Дополнительные сведения об этом представлены в разделе Разрешения поставщика контента.
Например, чтобы получить из поставщика пользовательского словаря список слов и языков, на которых они представлены, вызовите метод {@link android.content.ContentResolver#query ContentResolver.query()}. В свою очередь, метод {@link android.content.ContentResolver#query query()} вызывает метод {@link android.content.ContentProvider#query ContentProvider.query()}, определенный поставщиком пользовательского словаря. В примере кода ниже показан вызов метода {@link android.content.ContentResolver#query ContentResolver.query()}.
// Queries the user dictionary and returns results mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table mProjection, // The columns to return for each row mSelectionClause // Selection criteria mSelectionArgs, // Selection criteria mSortOrder); // The sort order for the returned rows
В таблице 2 указано соответствие аргументов для метода {@link android.content.ContentResolver#query query(Uri,projection,selection,selectionArgs,sortOrder)} SQL-инструкции SELECT.
Аргумент метода query() | Параметр/ключевое слово SELECT | Примечания |
---|---|---|
Uri |
FROM table_name |
Uri соответствует таблице table_name в поставщике. |
projection |
col,col,col,... |
projection представляет собой массив столбцов, которые следует включить
в каждую полученную строку.
|
selection |
WHERE col = value |
selection задает критерии для выбора строк. |
selectionArgs |
(Точный эквивалент отсутствует. В предложении выбора заполнители ?
заменяются аргументами выбора).
|
|
sortOrder |
ORDER BY col,col,... |
sortOrder задает порядок отображения строк в возвращаемом объекте
{@link android.database.Cursor}.
|
URI контента представляет собой URI, который определяет данные в поставщике. URI контента могут включать символическое имя всего поставщика (его центр) и имя, которое указывает на таблицу (путь). При вызове клиентского метода для доступа к таблице в поставщике URI контента этой таблицы выступает в роли одного из аргументов этого метода.
Константа {@link android.provider.UserDictionary.Words#CONTENT_URI} в предыдущих строках кода содержит URI контента таблицы words в пользовательском словаре. Объект{@link android.content.ContentResolver} анализирует центр URI и использует его для «разрешения» поставщика путем сравнения центра с системной таблицей известных поставщиков. {@link android.content.ContentResolver} может отправить аргументы запроса в соответствующий поставщик.
{@link android.content.ContentProvider} использует часть URI контента, в которой указан путь, для выбора таблицы для доступа. В поставщике обычно имеется путь для каждой предоставляемой им таблицы.
В предыдущих строках кода полный URI для таблицы words выглядит следующим образом:
content://user_dictionary/words
Строка user_dictionary
֪– это центр поставщика, а строка
words
— это путь к таблице. Строка
content://
(схема) присутствует всегда;
она определяет, что это URI контента.
Многие поставщики предоставляют доступ к одной строке в таблице путем добавления идентификатора
в конец URI. Например, чтобы извлечь из пользовательского словаря строку, в столбце _ID
которой
указано 4
, можно воспользоваться следующим URI контента:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Идентификаторы часто используются в случае, когда вы извлекли набор строк и хотите обновить или удалить одну из них.
Примечание. В классах {@link android.net.Uri} и {@link android.net.Uri.Builder} имеются методы для удобного создания правильно оформленных объектов URI из строк. {@link android.content.ContentUris} содержит методы для удобного добавления идентификаторов к URI. В примере кода выше для добавления идентификатора к URI контента UserDictionary используется метод {@link android.content.ContentUris#withAppendedId withAppendedId()}.
В это разделе рассматривается порядок получения данных от поставщика на примере поставщика пользовательского словаря.
Для полной ясности в примерах кода, приведенных в этом разделе, методы {@link android.content.ContentResolver#query ContentResolver.query()} вызываются в потоке пользовательского интерфейса. В реальном коде запросы следует выполнять асинхронно в отдельном потоке. Одним из способов реализовать это является использование класса {@link android.content.CursorLoader}, который более подробно описан в статье Загрузчики. Кроме того, в этой статье представлены лишь фрагменты кода; они не представляют собой готовое приложение.
Чтобы получить данные из поставщика, выполните указанные ниже основные действия.
Чтобы ваше приложение могло получать данные от поставщика, приложению требуется получить от поставщика разрешение
на чтение. Это разрешение невозможно получить во время выполнения; вместо этого вам необходимо указать, что вам требуется
такое разрешение, в манифесте приложения. Для этого воспользуйтесь элементом
<uses-permission>
и укажите точное название разрешения,
определенное поставщиком. Указав этот элемент в манифесте, вы тем самым запрашиваете
необходимое разрешение для вашего приложения. Когда пользователи устанавливают ваше приложение, они косвенно
получают разрешение по этому запросу.
Чтобы узнать точное название разрешения на чтение в используемом поставщике, а также названия других используемых в нем разрешений на чтение, обратитесь к документации поставщика.
Дополнительные сведения о роли разрешений в получении доступа к поставщику представлены в разделе Разрешения поставщика контента.
Поставщик пользовательского словаря задает разрешение
android.permission.READ_USER_DICTIONARY
в своем файле манифеста,
поэтому приложению, которому требуется выполнить чтение данных из поставщика, необходимо запросить именно это разрешение.
Следующим этапом получения данных от поставщика является создание запроса. В следующем фрагменте кода задаются некоторые переменные для доступа к поставщику пользовательского словаря:
// A "projection" defines the columns that will be 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 mSelectionClause = null; // Initializes an array to contain selection arguments String[] mSelectionArgs = {""};
В следующем фрагменте кода демонстрируется порядок использования метода {@link android.content.ContentResolver#query ContentResolver.query()} (в качестве примера выступает поставщик пользовательского словаря): Клиентский запрос поставщика аналогичен SQL-запросу. В нем содержится набор столбцов, которые возвращаются, набор критериев выборки и порядок сортировки.
Набор столбцов, которые должен возвратить запрос, называется проекцией
(переменная mProjection
).
Выражение, которое задает строки для получения, состоит из предложения выбора
и аргументов выбора. Предложение выбора представляет собой сочетание логических выражений,
имен столбцов и значений (переменная mSelectionClause
). Если вместо значения указать подставляемый параметр
?
, метод запроса извлекает значение из массива аргументов выбора (переменная
mSelectionArgs
).
В следующем фрагменте кода, если пользователь не указал слово, то для предложения выбора задается значение
null
, а запрос возвращает все слова, имеющиеся в поставщике. Если пользователь указал слово, то для предложения выбора задается значение
UserDictionary.Words.WORD + " = ?"
,
а для первого элемента в массиве аргументов выбора задается введенное пользователем слово.
/* * This defines a one-element String array to contain the selection argument. */ String[] mSelectionArgs = {""}; // Gets a word from the UI mSearchString = mSearchWord.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(mSearchString)) { // Setting the selection clause to null will return all words mSelectionClause = null; mSelectionArgs[0] = ""; } else { // Constructs a selection clause that matches the word that the user entered. mSelectionClause = UserDictionary.Words.WORD + " = ?"; // Moves the user's input string to the selection arguments. mSelectionArgs[0] = mSearchString; } // 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 mProjection, // The columns to return for each row mSelectionClause // Either null, or the word the user entered mSelectionArgs, // Either empty, or the string the user entered mSortOrder); // 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 may want to * 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 was unsuccessful. This isn't necessarily * an error. You may 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 }
Этот запрос аналогичен следующей инструкции SQL:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
В этой инструкции SQL вместо констант класса-контракта используются фактические имена столбцов.
Если данные, которыми управляет поставщик контента, находятся в базе данных SQL, то включение в необработанные инструкции SQL внешних ненадежных данных может привести к атаке путем внедрения кода SQL.
Рассмотрим следующее предложение выбора:
// Constructs a selection clause by concatenating the user's input to the column name String mSelectionClause = "var = " + mUserInput;
Если вы используете это предложение, вы разрешаете пользователю связать вашу инструкцию SQL с вредоносным кодом SQL.
Например, пользователь может ввести nothing; DROP TABLE *; для mUserInput
, что
приведет к созданию следующего предложения выбора: var = nothing; DROP TABLE *;
. Поскольку
предложение выбора выполняется в потоке как инструкция SQL, это может привести к тому, что поставщик удалит все
таблицы в соответствующей базе данных SQLite (если только в поставщик не настроен на отслеживание попыток
внедрить вредоносный код SQL).
Чтобы избежать этого, воспользуйтесь предложением выбора, в котором ?
выступает в качестве подставляемого
параметра, а также отдельным массивом аргументов выбора. После этого ввод пользователя
будет связан напрямую с запросом и не будет интерпретироваться как часть инструкции SQL.
Поскольку в этом случае введенный пользователем запрос не рассматривается как код SQL, то в него не удастся внедрить вредоносный код SQL. Вместо
объединения, которое следует включить в пользовательский ввод, используйте следующее предложение выбора:
// Constructs a selection clause with a replaceable parameter String mSelectionClause = "var = ?";
Настройте массив аргументов выбора следующим образом:
// Defines an array to contain the selection arguments String[] selectionArgs = {""};
Укажите значение для массива аргументов выбора:
// Sets the selection argument to the user's input selectionArgs[0] = mUserInput;
Предложение выбора, в котором ?
используется в качестве подстановочного параметра, и массив
аргументов выбора представляют собой предпочтительный способ указания выбора, даже если поставщик
не использует базу данных SQL.
Клиентский метод {@link android.content.ContentResolver#query ContentResolver.query()} всегда возвращает объект {@link android.database.Cursor}, содержащий столбцы, указанные в проекции запроса для строк, которые соответствуют критериям выборки в запросе. Объект {@link android.database.Cursor} предоставляет прямой доступ на чтение содержащихся в нем строк и столбцов. С помощью методов {@link android.database.Cursor} можно выполнить итерацию по строкам в результатах, определить тип данных для каждого столбца, получить данные из столбца, а также проверить другие свойства результатов. Некоторые реализации объекта {@link android.database.Cursor} автоматически обновляют объект при изменении данных в поставщике или запускают выполнение методов в объекте-наблюдателе при изменении объекта{@link android.database.Cursor}, либо выполняют и то, и другое.
Примечание. Поставщик может ограничить доступ к столбцам на основе характера объекта, выполняющего запрос. Например, поставщик контактов ограничивает доступ адаптеров синхронизации к некоторым столбцам, поэтому он не возвращает их в операцию или службу.
Если строки, соответствующие критериям выборки, отсутствуют, поставщик возвращает объект{@link android.database.Cursor}, в котором для метода {@link android.database.Cursor#getCount Cursor.getCount()} указано значение «0» (пустой объект cursor).
При возникновении внутренней ошибки результаты запроса зависят от определенного поставщика. Поставщик может
возвратитьnull
или выдать {@link java.lang.Exception}.
Поскольку {@link android.database.Cursor} представляет собой «список» строк, то наилучшим способом отобразить содержимое объекта {@link android.database.Cursor} будет связать его с {@link android.widget.ListView} посредством {@link android.widget.SimpleCursorAdapter}.
Следующий фрагмент кода является продолжением предыдущего фрагмента. Он создает объект {@link android.widget.SimpleCursorAdapter}, содержащий объект{@link android.database.Cursor}, который был получен в запросе, а затем определяет этот объект в качестве адаптера для {@link android.widget.ListView}:
// Defines a list of columns to retrieve from the Cursor and load into an output row String[] mWordListColumns = { 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 will receive the Cursor columns for each row int[] mWordListItems = { R.id.dictWord, R.id.locale}; // Creates a new SimpleCursorAdapter mCursorAdapter = 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 mWordListColumns, // A string array of column names in the cursor mWordListItems, // An integer array of view IDs in the row layout 0); // Flags (usually none are needed) // Sets the adapter for the ListView mWordList.setAdapter(mCursorAdapter);
Примечание. Чтобы вернуть {@link android.widget.ListView} с объектом
{@link android.database.Cursor}, объект cursor должен содержать столбец с именем _ID
.
Поэтому показанный ранее запрос извлекает столбец_ID
для таблицы
words, даже если {@link android.widget.ListView} не отображает ее.
Данное ограничение также объясняет, почему в каждой таблице поставщика имеется столбец
_ID
.
Вместо того, чтобы просто отобразить результаты запроса, вы можете использовать их для выполнения других задач. Например, можно получить написание слов из пользовательского словаря, а затем выполнить их поиск в других поставщиках. Для этого выполните итерацию по строкам в объекте {@link android.database.Cursor}:
// 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 may 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 will 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. }
Реализации объекта {@link android.database.Cursor} содержат несколько методов get для получения из объекта различных типов данных. Например, в следующем фрагменте кода используется метод {@link android.database.Cursor#getString getString()}. В них также имеется метод {@link android.database.Cursor#getType getType()}, который возвращает значение, указывающее на тип данных в столбце.
Приложение поставщика может задавать разрешения, которые требуются другим приложениям для доступа к данным в поставщике. Такие разрешения гарантируют, что пользователь знает, к каким данным приложение будет пытаться получить доступ. На основе требований поставщика другие приложения запрашивают разрешения, которые требуются им для доступа к поставщику. Конечные пользователи видят запрошенные разрешения при установке приложения.
Если приложение поставщика не задает никаких разрешений, другие приложения не получают доступ к данным поставщика. Однако компонентам приложения поставщика всегда предоставлен полный доступ на чтение и запись, независимо от заданных разрешений.
Как уже было отмечено ранее, для получения данных из поставщика пользовательского словаря требуется разрешение
android.permission.READ_USER_DICTIONARY
.
В поставщике предусмотрено отдельное разрешениеandroid.permission.WRITE_USER_DICTIONARY
для вставки, обновления или удаления данных.
Чтобы получить разрешения, необходимые для доступа к поставщику, приложение запрашивает их с помощью элемента
<uses-permission>
в файле манифеста. При установке менеджером пакетов Android приложения пользователю необходимо
утвердить все разрешения, запрашиваемые приложением. В случае утверждения всех разрешений
менеджер пакетов продолжает установку; если же пользователь отклоняет их, менеджер
пакетов отменяет установку.
Для запроса доступа на чтение данных в поставщике пользовательского словаря используется
следующий элемент
<uses-permission>
:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Дополнительные сведения о влиянии разрешений на доступ к поставщику представлены в статье Безопасность и разрешения.
Подобно тому, как вы получаете данные от поставщика, вы также можете можете использовать возможности взаимодействия между клиентом поставщика и объектом {@link android.content.ContentProvider} поставщика для изменения данных. Можно вызвать метод объекта {@link android.content.ContentResolver}, указав аргументы, которые были переданы в соответствующий метод объекта {@link android.content.ContentProvider}. Поставщик и клиент поставщика автоматически обрабатывают взаимодействие между процессами и обеспечивают безопасность.
Для вставки данных в поставщик вызовите метод {@link android.content.ContentResolver#insert ContentResolver.insert()}. Этот метод вставляет новую строку в поставщик и возвращает URI контента для этой строки. В следующем фрагменте кода демонстрируется порядок вставки нового слова в поставщик пользовательского словаря:
// Defines a new Uri object that receives the result of the insertion Uri mNewUri; ... // Defines an object to contain the new values to insert ContentValues mNewValues = new ContentValues(); /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value" */ mNewValues.put(UserDictionary.Words.APP_ID, "example.user"); mNewValues.put(UserDictionary.Words.LOCALE, "en_US"); mNewValues.put(UserDictionary.Words.WORD, "insert"); mNewValues.put(UserDictionary.Words.FREQUENCY, "100"); mNewUri = getContentResolver().insert( UserDictionary.Word.CONTENT_URI, // the user dictionary content URI mNewValues // the values to insert );
Данные для новой строки поступают в один объект {@link android.content.ContentValues},
который аналогичен объекту cursor с одной строкой. Столбцы в этом объекте необязательно
должны содержать данные такого же типа, и если вы вообще не собираетесь указывать значение, вы можете задать для столбца значение
null
с помощью метода {@link android.content.ContentValues#putNull ContentValues.putNull()}.
Код в представленном фрагменте не добавляет столбец _ID
, поскольку этот столбец сохраняется
автоматически. Поставщик присваивает уникальное значение _ID
каждой
добавляемой строке. Обычно поставщики используют это значение в качестве основного ключа таблицы.
URI контента, возвращенный в элементе newUri
, служит для идентификации новой добавленной строки
в следующем формате:
content://user_dictionary/words/<id_value>
<id_value>
— это содержимое столбца _ID
для новой строки.
Большинство поставщиков автоматически определяют эту форму URI контента, а затем
выполняют запрошенную операцию с требуемой строкой.
Чтобы получить значение _ID
из возвращенного объекта {@link android.net.Uri}, вызовите метод
{@link android.content.ContentUris#parseId ContentUris.parseId()}.
Чтобы обновить строку, используйте объект {@link android.content.ContentValues} с обновленными
значениями (точно так же, как вы это делаете при вставке) и критериями выборки (так же, как и с запросом).
Используемый вами клиентский метод называется
{@link android.content.ContentResolver#update ContentResolver.update()}. Вам не нужно добавлять значения в объект
{@link android.content.ContentValues} для обновляемых столбцов. Чтобы очистить содержимое столбца, задайте значение
null
.
Следующий фрагмент кода служит для изменения языка во всех строках, где в качестве языка указано en, на
null
. Возвращаемое значение представляет собой количество строк, которые были обновлены:
// Defines an object to contain the updated values ContentValues mUpdateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?"; String[] mSelectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int mRowsUpdated = 0; ... /* * Sets the updated value and updates the selected words. */ mUpdateValues.putNull(UserDictionary.Words.LOCALE); mRowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mUpdateValues // the columns to update mSelectionClause // the column to select on mSelectionArgs // the value to compare to );
Также следует проверить пользовательский ввод при вызове метода {@link android.content.ContentResolver#update ContentResolver.update()}. Дополнительные сведения об этом представлены в разделе Защита от ввода вредоносного кода.
Удаление данных аналогично получению данных строки: необходимо указать критерии выборки для строк, которые требуется удалить, после чего клиентский метод возвратит количество удаленных строк. Ниже представлен фрагмент кода для удаления строк с идентификатором appid user. Метод возвращает количество удаленных строк.
// Defines selection criteria for the rows you want to delete String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] mSelectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int mRowsDeleted = 0; ... // Deletes the words that match the selection criteria mRowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mSelectionClause // the column to select on mSelectionArgs // the value to compare to );
Также следует проверить пользовательский ввод при вызове метода {@link android.content.ContentResolver#delete ContentResolver.delete()}. Дополнительные сведения об этом представлены в разделе Защита от ввода вредоносного кода.
Поставщики контента могут предоставлять различные тип данных. Поставщик пользовательского словаря предоставляет только текст, но также может предоставлять следующие форматы:
Другим типом данных, предлагаемых поставщиком, является большой двоичный объект (BLOB), реализованный как 64-разрядный массив. Чтобы просмотреть доступные типы данных, обратитесь к методам get класса {@link android.database.Cursor}.
Тип данных для каждого столбца в поставщике обычно указывается в документации к поставщику. Типы данных для поставщика пользовательского словаря указаны в справочной документации для класса-контракта {@link android.provider.UserDictionary.Words} (дополнительные сведения о классах-контрактах представлены в разделе Классы-контракты). Также определить тип данных можно путем вызова метода {@link android.database.Cursor#getType Cursor.getType()}.
Поставщики также хранят информацию о типе данных MIME для каждого определяемого ими URI контента. Эту информацию можно использовать для определения того, может ли ваше приложение обрабатывать предлагаемые поставщиком данные, а также для выбора типа обработки на основе типа MIME. Информация о типе MIME обычно требуется при работе с поставщиком, который содержит сложные структуры данных или файлы. Например, в таблице{@link android.provider.ContactsContract.Data} в поставщике контактов используются типы MIME для отметки типа данных контакта, которые хранятся в каждой строке. Чтобы получить тип MIME, соответствующий URI контента, вызовите метод {@link android.content.ContentResolver#getType ContentResolver.getType()}.
Синтаксис стандартных и настраиваемых типов MIME описан в справке по типам MIME.
При разработке приложения следует учитывать три альтернативных формы доступа к поставщику:
Пакетный доступ и изменение с помощью намерений описаны в следующих разделах.
Пакетный доступ к поставщику полезно использовать в случаях, когда необходимо вставить большое количество строк, или для вставки строк в несколько таблиц в рамках одного вызова метода, а также в общих случаях для выполнения ряда операций на границах процессов в виде транзакции (атомарной операции).
Для доступа к поставщику в «пакетном режиме» необходимо создать массив объектов {@link android.content.ContentProviderOperation}, а затем отправить их в поставщик контента с помощью метода {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. В этот метод необходимо передать центр поставщика контента, а не определенный URI контента. Это позволит каждому объекту {@link android.content.ContentProviderOperation} в массиве взаимодействовать с разными таблицами. Метод {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} возвращает массив результатов.
В описании класса-контракта {@link android.provider.ContactsContract.RawContacts}
также представлен фрагмент кода, в котором демонстрируется вставка в пакетном режиме. В исходном файле ContactAdder.java
примера приложения
Диспетчер контактов
имеется пример пакетного
доступа.
Если вашему приложению не предоставлены разрешения, вы по-прежнему можете воспользоваться намерением для отображения данных в другом приложении. Например, приложение «Календарь» принимает намерение {@link android.content.Intent#ACTION_VIEW}, которое позволяет отобразить определенную дату или событие. Благодаря этому информацию календаря можно отображать без необходимости создавать собственный пользовательский интерфейс. Дополнительные сведения об этой функции представлены в статье Поставщик календаря.
Приложение, в которое вы отправляете намерение, не обязательно должно быть связано с поставщиком. Например, в поставщике контактов можно создать форму контакта, а затем отправить намерение {@link android.content.Intent#ACTION_VIEW}, содержащее URI контента для изображения контакта, в средство просмотра изображений.
Намерения позволяют в обход получать доступ к поставщику контента. Вы можете разрешить пользователям доступ к данным в поставщике даже в том случае, если у приложения отсутствуют разрешения на доступ, либо путем получения результирующего намерения от приложения, у которого имеются необходимые разрешения, либо путем активации приложения, у которого имеются разрешения и которое разрешает пользователю работать с ним.
Вы можете получить доступ к данным в поставщике контента даже тогда, когда у вас нет необходимых разрешений на доступ , путем отправки намерения в приложение, у которого есть такие разрешения, и получения результирующего намерения, которое содержит разрешения URI. Эти разрешения для определенного URI контента действуют до тех пор, пока не будет завершена операция, получившая их. Приложение, у которой имеются бессрочные разрешения, предоставляет временные разрешения путем задания соответствующего флага в результирующем намерении:
Примечание. Эти флаги не предоставляют общий доступ на чтение или запись поставщику, центр которого указан в URI контента. Доступ предоставляется только самому URI.
Поставщик определяет разрешения URI для URI контента в своем манифесте с помощью атрибута
android:grantUriPermission
элемента
<provider>
,
а также
с помощью дочернего элемента
<grant-uri-permission>
элемента
<provider>
. Дополнительные сведения о механизме разрешений URI представлены в статье
Безопасность и разрешения в разделе
Разрешения URI.
Например, можно получить данные о контакте из поставщика контактов, даже если у вас нет разрешения {@link android.Manifest.permission#READ_CONTACTS}. Возможно, это потребуется реализовать в приложении, которое отправляет электронные поздравления контакту в день его рождения. Вместо запроса {@link android.Manifest.permission#READ_CONTACTS}, когда вы получаете доступ ко всем контактам пользователя и всей информации о них, можно предоставить пользователю возможность указать, какие контакты используются вашим приложением. Для этого воспользуйтесь указанным ниже процессом.
Простой способ разрешить пользователю изменять данные, на доступ к которым у вас нет доступа — это активировать приложение, у которого есть такие разрешения, а затем предоставить пользователю возможность выполнять необходимые действия в этом приложении.
Например, приложение «Календарь» принимает намерения {@link android.content.Intent#ACTION_INSERT}, с помощью которого можно активировать пользовательский интерфейс приложения для вставки. Вы можете передать в это намерение дополнительные данные, которые приложение использует для заполнения полей в пользовательском интерфейсе. Поскольку синтаксис повторяющихся событий довольно сложный, то события предпочтительно вставлять в поставщик календаря путем активации приложения «Календарь» с помощью действия {@link android.content.Intent#ACTION_INSERT} и последующего предоставления пользователю возможности самому вставить событие в этом приложении.
Класс-контракт определяет константы, которые обеспечивают для приложений возможность работать с URI контента, именами столбцов, операциями намерения и другими функциями поставщика контента. Классы-контракты не включены в поставщик; разработчику поставщика следует определить их и сделать их доступными для других разработчиков. Многие из поставщиков, включенные в платформу Android, содержат соответствующие классы-контракты в пакете {@link android.provider}.
Например, в поставщике пользовательского календаря имеется класс-контракт {@link android.provider.UserDictionary}, содержащий константы URI контента и имен столбцов. URI контента для таблицы words определен в константе {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. В классе {@link android.provider.UserDictionary.Words} также имеются константы имен столбцов, которые используются в фрагментах кода примера приложения, представленных в этой статье. Например, проекцию запроса можно определить следующим образом:
String[] mProjection = { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE };
Другим классом-контрактом является класс {@link android.provider.ContactsContract} для поставщика контактов. В справочной документации к этому классу представлены фрагменты кода примера приложения. Один из его подклассов, {@link android.provider.ContactsContract.Intents.Insert}, представляет собой класс-контракт, который содержит константы для намерений и их данных.
Поставщики контента могут возвращать как стандартные типы мультимедиа MIME, так и строки с настраиваемым типом MIME, либо оба этих типа.
Типы MIME имеют следующий формат:
type/subtype
Например, хорошо известный тип MIME text/html
имеет тип text
и подтип
html
. Если поставщик возвращает этот тип URI, это означает, что
строка запроса, в которой используется этот URI, возвратит текста с тегами HTML.
Строки с настраиваемым типом MIME, которые также называются типами MIME поставщика, имеют более сложные значения типов и подтипов. Значение типа всегда следующее:
vnd.android.cursor.dir
для нескольких строк, или
vnd.android.cursor.item
для одной строки.
Подтип зависит от поставщика. Встроенные поставщики Android обычно содержат простой подтип. Например, когда приложение «Контакты» создает строку для номера телефона, оно задает следующий тип MIME в этой строке:
vnd.android.cursor.item/phone_v2
Обратите внимание, что значение подтипа просто phone_v2
.
Разработчики поставщиков могут создавать свои собственные шаблоны подтипов на основе
центра и названий таблиц поставщика. Например, рассмотрим поставщик, который содержит расписание движения поездов.
Центром поставщика является com.example.trains
, в котором содержатся таблицы
Line1, Line2 и Line3. В ответ на следующий URI контента
content://com.example.trains/Line1
для таблицы Line1 поставщик возвращает следующий тип MIME
vnd.android.cursor.dir/vnd.example.line1
В ответ на следующий URI контента
content://com.example.trains/Line2/5
для строки 5 в таблице Line2 поставщик возвращает следующий тип MIME
vnd.android.cursor.item/vnd.example.line2
В большинстве поставщиков контента определены константы класса-контракта для используемых в них типов MIME. Например, класс-контракт {@link android.provider.ContactsContract.RawContacts} поставщика контактов определяет константу {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} для типа MIME одной строки необработанного контакта.
URI контента для единичных строк описываются в разделе URI контента.