• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Основные сведения о поставщике контента
2@jd:body
3<div id="qv-wrapper">
4<div id="qv">
5<!-- In this document -->
6<h2>Содержание документа</h2>
7<ol>
8    <li>
9        <a href="#Basics">Обзор</a>
10        <ol>
11            <li>
12                <a href="#ClientProvider">Доступ к поставщику</a>
13            </li>
14            <li>
15                <a href="#ContentURIs">URI контента</a>
16            </li>
17        </ol>
18    </li>
19    <li>
20        <a href="#SimpleQuery">Получение данных от поставщика</a>
21        <ol>
22            <li>
23                <a href="#RequestPermissions">Запрос разрешения на чтение</a>
24            </li>
25            <li>
26                <a href="#Query">Создание запроса</a>
27            </li>
28            <li>
29                <a href="#DisplayResults">Отображение результатов запроса</a>
30            </li>
31            <li>
32                <a href="#GettingResults">Получение данных из результатов запроса</a>
33            </li>
34        </ol>
35    </li>
36    <li>
37        <a href="#Permissions">Разрешения поставщика контента</a>
38    </li>
39    <li>
40        <a href="#Modifications">Вставка, обновление и удаление данных</a>
41        <ol>
42            <li>
43                <a href="#Inserting">Вставка данных</a>
44            </li>
45            <li>
46                <a href="#Updating">Обновление данных</a>
47            </li>
48            <li>
49                <a href="#Deleting">Удаление данных</a>
50            </li>
51        </ol>
52    </li>
53    <li>
54        <a href="#DataTypes">Типы поставщиков данных</a>
55    </li>
56    <li>
57        <a href="#AltForms">Альтернативные формы доступа к поставщику</a>
58        <ol>
59            <li>
60                <a href="#Batch">Пакетный доступ</a>
61            </li>
62            <li>
63                <a href="#Intents">Доступ к данным с помощью намерений</a>
64            </li>
65        </ol>
66    </li>
67    <li>
68        <a href="#ContractClasses">Классы-контракты</a>
69    </li>
70    <li>
71        <a href="#MIMETypeReference">Справка по типам MIME</a>
72    </li>
73</ol>
74
75    <!-- Key Classes -->
76<h2>Ключевые классы</h2>
77    <ol>
78        <li>
79            {@link android.content.ContentProvider}
80        </li>
81        <li>
82            {@link android.content.ContentResolver}
83        </li>
84        <li>
85            {@link android.database.Cursor}
86        </li>
87        <li>
88            {@link android.net.Uri}
89        </li>
90    </ol>
91
92    <!-- Related Samples -->
93<h2>Связанные примеры</h2>
94    <ol>
95        <li>
96        <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List2.html">
97        Cursor (People)</a>
98        </li>
99        <li>
100        <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html">
101        Cursor (Phones)</a>
102        </li>
103    </ol>
104
105    <!-- See also -->
106<h2>См. также</h2>
107    <ol>
108        <li>
109            <a href="{@docRoot}guide/topics/providers/content-provider-creating.html">
110Создание поставщика контента</a>
111        </li>
112        <li>
113            <a href="{@docRoot}guide/topics/providers/calendar-provider.html">
114Поставщик календаря</a>
115        </li>
116    </ol>
117</div>
118</div>
119
120    <!-- Intro paragraphs -->
121<p>
122    Поставщик контента управляет доступом к центральному репозиторию данных. Поставщик
123является компонентом приложения Android, который зачастую имеет собственный пользовательский интерфейс для
124работы с данными. Однако поставщики контента предназначены в первую очередь для использования другими приложениями,
125которые получают доступ к поставщику посредством клиентского объекта поставщика. Вместе поставщики
126и клиенты поставщиков обеспечивают согласованный, стандартный интерфейс к данным, который также обрабатывает
127взаимодействие между процессами и обеспечивает защищенный доступ к данным.
128</p>
129<p>
130    В этой статье рассматриваются основные сведения, касающиеся следующих тем:
131</p>
132    <ul>
133        <li>принцип работы поставщика контента;</li>
134        <li>API, используемый для получения данных от поставщика контента;</li>
135        <li>API, используемый для вставки данных в поставщик контента и их обновления или удаления в нем;</li>
136        <li>другие функции API, которые упрощают работу с поставщиками.</li>
137    </ul>
138
139    <!-- Basics -->
140<h2 id="Basics">Обзор</h2>
141<p>
142    Поставщик контента предоставляет данные внешним приложениям в виде одной или нескольких таблиц,
143аналогичных таблицам в реляционной базе данных. Строка представляет собой экземпляр некоторого типа
144собираемых поставщиком данных, а каждый столбец в этой строке — это отдельный элемент данных,
145собранных для экземпляра.
146</p>
147<p>
148    Примером встроенного поставщика в платформе Android может служить пользовательский словарь,
149в котором хранятся данные о написании нестандартных слов, добавленных пользователем. В таблице 1 показано,
150как данные могут выглядеть в этой таблице поставщика.
151</p>
152<p class="table-caption">
153    <strong>Таблица 1.</strong> Пример таблицы пользовательского словаря.
154</p>
155<table id="table1" style="width: 50%;">
156    <tr>
157        <th style="width:20%" align="center" scope="col">word</th>
158        <th style="width:20%" align="center" scope="col">app id</th>
159        <th style="width:20%" align="center" scope="col">frequency</th>
160        <th style="width:20%" align="center" scope="col">locale</th>
161        <th style="width:20%" align="center" scope="col">_ID</th>
162    </tr>
163    <tr>
164        <td align="center" scope="row">mapreduce</td>
165        <td align="center">user1</td>
166        <td align="center">100</td>
167        <td align="center">en_US</td>
168        <td align="center">1</td>
169    </tr>
170    <tr>
171        <td align="center" scope="row">precompiler</td>
172        <td align="center">user14</td>
173        <td align="center">200</td>
174        <td align="center">fr_FR</td>
175        <td align="center">2</td>
176    </tr>
177    <tr>
178        <td align="center" scope="row">applet</td>
179        <td align="center">user2</td>
180        <td align="center">225</td>
181        <td align="center">fr_CA</td>
182        <td align="center">3</td>
183    </tr>
184    <tr>
185        <td align="center" scope="row">const</td>
186        <td align="center">user1</td>
187        <td align="center">255</td>
188        <td align="center">pt_BR</td>
189        <td align="center">4</td>
190    </tr>
191    <tr>
192        <td align="center" scope="row">int</td>
193        <td align="center">user5</td>
194        <td align="center">100</td>
195        <td align="center">en_UK</td>
196        <td align="center">5</td>
197    </tr>
198</table>
199<p>
200    В каждой строке таблицы 1 представлен экземпляр слова, которое отсутствует
201в стандартном словаре. В каждом ее столбце содержатся некоторые данные для слова,
202например, данные о языке, на котором это слово было впервые использовано. Заголовки столбцов представляют собой имена столбцов, которые хранятся
203в поставщике. Чтобы узнать язык строки, необходимо обратиться к столбцу <code>locale</code>. В этом
204поставщике столбец <code>_ID</code> выступает в роли «основного ключа»,
205который поставщик автоматически сохраняет.
206</p>
207<p class="note">
208    <strong>Примечание.</strong> В поставщике необязательно должен быть основной ключ, а также ему необязательно
209использовать<code>_ID</code> в качестве имени столбца основного ключа, если таковой имеется. Однако, если
210необходимо привязать данные из поставщика к классу {@link android.widget.ListView},
211один из столбцов должен именоваться <code>_ID</code>. Дополнительные сведения об этом требовании представлены в разделе
212<a href="#DisplayResults">Отображение результатов запроса</a>.
213</p>
214<h3 id="ClientProvider">Доступ к поставщику</h3>
215<p>
216    Для доступа приложения к данным из поставщика контента
217используется клиентский объект {@link android.content.ContentResolver}. В этом объекте имеются методы, которые вызывают
218идентичные методы в объекте поставщика, который представляет собой экземпляр одного из конкретных
219подклассов класса {@link android.content.ContentProvider}. В этих методах
220{@link android.content.ContentResolver} представлены основные функции
221 CRUD (аббревиатура create, retrieve, update, delete [создание, получение, обновление и удаление]) постоянного хранилища.
222</p>
223<p>
224    Объект {@link android.content.ContentResolver} в процессе клиентского приложения
225и объект {@link android.content.ContentProvider} в приложении,
226которое владеет поставщиком, автоматически обрабатывают взаимодействие между процессами.
227Объект {@link android.content.ContentProvider} также выступает в роли уровня абстракции между
228репозиторием данных и внешним представлением данных в виде таблиц.
229</p>
230<p class="note">
231    <strong>Примечание.</strong> Для доступа к поставщику ваше приложение обычно должно запросить определенные разрешения
232в своем файле манифеста. Дополнительные сведения об этом представлены в разделе
233<a href="#Permissions">Разрешения поставщика контента</a>.
234</p>
235<p>
236    Например, чтобы получить из поставщика пользовательского словаря список слов и языков, на которых они представлены,
237вызовите метод {@link android.content.ContentResolver#query ContentResolver.query()}.
238    В свою очередь, метод {@link android.content.ContentResolver#query query()} вызывает метод
239{@link android.content.ContentProvider#query ContentProvider.query()}, определенный поставщиком
240пользовательского словаря. В примере кода ниже показан вызов метода
241{@link android.content.ContentResolver#query ContentResolver.query()}.
242<p>
243<pre>
244// Queries the user dictionary and returns results
245mCursor = getContentResolver().query(
246    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
247    mProjection,                        // The columns to return for each row
248    mSelectionClause                    // Selection criteria
249    mSelectionArgs,                     // Selection criteria
250    mSortOrder);                        // The sort order for the returned rows
251</pre>
252<p>
253    В таблице 2 указано соответствие аргументов для метода
254{@link android.content.ContentResolver#query
255query(Uri,projection,selection,selectionArgs,sortOrder)} SQL-инструкции SELECT.
256</p>
257<p class="table-caption">
258    <strong>Таблица 2.</strong> Сравнение метода query() и SQL-запроса.
259</p>
260<table id="table2" style="width: 75%;">
261    <tr>
262        <th style="width:25%" align="center" scope="col">Аргумент метода query()</th>
263        <th style="width:25%" align="center" scope="col">Параметр/ключевое слово SELECT</th>
264        <th style="width:50%" align="center" scope="col">Примечания</th>
265    </tr>
266    <tr>
267        <td align="center"><code>Uri</code></td>
268        <td align="center"><code>FROM <em>table_name</em></code></td>
269        <td><code>Uri</code> соответствует таблице <em>table_name</em> в поставщике.</td>
270    </tr>
271    <tr>
272        <td align="center"><code>projection</code></td>
273        <td align="center"><code><em>col,col,col,...</em></code></td>
274        <td>
275            <code>projection</code> представляет собой массив столбцов, которые следует включить
276в каждую полученную строку.
277        </td>
278    </tr>
279    <tr>
280        <td align="center"><code>selection</code></td>
281        <td align="center"><code>WHERE <em>col</em> = <em>value</em></code></td>
282        <td><code>selection</code> задает критерии для выбора строк.</td>
283    </tr>
284    <tr>
285        <td align="center"><code>selectionArgs</code></td>
286        <td align="center">
287            (Точный эквивалент отсутствует. В предложении выбора заполнители <code>?</code>
288заменяются аргументами выбора).
289        </td>
290    </tr>
291    <tr>
292        <td align="center"><code>sortOrder</code></td>
293        <td align="center"><code>ORDER BY <em>col,col,...</em></code></td>
294        <td>
295            <code>sortOrder</code> задает порядок отображения строк в возвращаемом объекте
296{@link android.database.Cursor}.
297        </td>
298    </tr>
299</table>
300<h3 id="ContentURIs">URI контента</h3>
301<p>
302    <strong>URI контента</strong> представляет собой URI, который определяет данные в поставщике. URI контента
303могут включать символическое имя всего поставщика (его <strong>центр</strong>) и
304имя, которое указывает на таблицу (<strong>путь</strong>). При вызове
305клиентского метода для доступа к таблице в поставщике URI контента этой таблицы выступает в роли одного
306из аргументов этого метода.
307</p>
308<p>
309    Константа
310{@link android.provider.UserDictionary.Words#CONTENT_URI} в предыдущих строках кода содержит URI контента
311таблицы words в пользовательском словаре. Объект{@link android.content.ContentResolver}
312анализирует центр URI и использует его для «разрешения» поставщика
313путем сравнения центра с системной таблицей известных поставщиков. {@link android.content.ContentResolver}
314может отправить аргументы запроса в соответствующий
315поставщик.
316</p>
317<p>
318    {@link android.content.ContentProvider} использует часть URI контента, в которой указан путь, для выбора таблицы
319для доступа. В поставщике обычно имеется <strong>путь</strong> для каждой предоставляемой им таблицы.
320</p>
321<p>
322    В предыдущих строках кода полный URI для таблицы words выглядит следующим образом:
323</p>
324<pre>
325content://user_dictionary/words
326</pre>
327<p>
328    Строка <code>user_dictionary</code> ֪– это центр поставщика, а строка
329<code>words</code> — это путь к таблице. Строка
330<code>content://</code> (<strong>схема</strong>) присутствует всегда;
331она определяет, что это URI контента.
332</p>
333<p>
334    Многие поставщики предоставляют доступ к одной строке в таблице путем добавления идентификатора
335в конец URI. Например, чтобы извлечь из пользовательского словаря строку, в столбце <code>_ID</code> которой
336указано <code>4</code>, можно воспользоваться следующим URI контента:
337</p>
338<pre>
339Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
340</pre>
341<p>
342    Идентификаторы часто используются в случае, когда вы извлекли набор строк и хотите обновить или удалить
343одну из них.
344</p>
345<p class="note">
346    <strong>Примечание.</strong> В классах {@link android.net.Uri} и {@link android.net.Uri.Builder}
347имеются методы для удобного создания правильно оформленных объектов URI из строк. {@link android.content.ContentUris}
348содержит методы для удобного добавления идентификаторов
349к URI. В примере кода выше для добавления идентификатора к URI контента UserDictionary используется метод {@link android.content.ContentUris#withAppendedId
350withAppendedId()}.
351</p>
352
353
354    <!-- Retrieving Data from the Provider -->
355<h2 id="SimpleQuery">Получение данных от поставщика</h2>
356<p>
357    В это разделе рассматривается порядок получения данных от поставщика на примере
358поставщика пользовательского словаря.
359</p>
360<p class="note">
361    Для полной ясности в примерах кода, приведенных в этом разделе, методы
362{@link android.content.ContentResolver#query ContentResolver.query()} вызываются в потоке пользовательского интерфейса. В реальном
363коде запросы следует выполнять асинхронно в отдельном потоке. Одним из способов реализовать
364это является использование класса {@link android.content.CursorLoader}, который более подробно описан в
365статье
366<a href="{@docRoot}guide/components/loaders.html">Загрузчики</a>. Кроме того, в этой статье представлены лишь фрагменты кода; они не представляют собой готовое
367приложение.
368</p>
369<p>
370    Чтобы получить данные из поставщика, выполните указанные ниже основные действия.
371</p>
372<ol>
373   <li>
374        Запросите у поставщика разрешение на чтение.
375   </li>
376   <li>
377        Определите код, который отвечает за отправку запроса поставщику.
378   </li>
379</ol>
380<h3 id="RequestPermissions">Запрос разрешения на чтение</h3>
381<p>
382    Чтобы ваше приложение могло получать данные от поставщика, приложению требуется получить от поставщика разрешение
383на чтение. Это разрешение невозможно получить во время выполнения; вместо этого вам необходимо указать, что вам требуется
384такое разрешение, в манифесте приложения. Для этого воспользуйтесь элементом
385<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
386и укажите точное название разрешения,
387определенное поставщиком. Указав этот элемент в манифесте, вы тем самым запрашиваете
388необходимое разрешение для вашего приложения. Когда пользователи устанавливают ваше приложение, они косвенно
389получают разрешение по этому запросу.
390</p>
391<p>
392    Чтобы узнать точное название разрешения на чтение в используемом поставщике,
393а также названия других используемых в нем разрешений на чтение, обратитесь
394к документации поставщика.
395</p>
396<p>
397    Дополнительные сведения о роли разрешений в получении доступа к поставщику представлены в разделе
398<a href="#Permissions">Разрешения поставщика контента</a>.
399</p>
400<p>
401    Поставщик пользовательского словаря задает разрешение
402<code>android.permission.READ_USER_DICTIONARY</code> в своем файле манифеста,
403поэтому приложению, которому требуется выполнить чтение данных из поставщика, необходимо запросить именно это разрешение.
404</p>
405<!-- Constructing the query -->
406<h3 id="Query">Создание запроса</h3>
407<p>
408    Следующим этапом получения данных от поставщика является создание запроса. В следующем фрагменте кода
409задаются некоторые переменные для доступа к поставщику пользовательского словаря:
410</p>
411<pre class="prettyprint">
412
413// A "projection" defines the columns that will be returned for each row
414String[] mProjection =
415{
416    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
417    UserDictionary.Words.WORD,   // Contract class constant for the word column name
418    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
419};
420
421// Defines a string to contain the selection clause
422String mSelectionClause = null;
423
424// Initializes an array to contain selection arguments
425String[] mSelectionArgs = {""};
426
427</pre>
428<p>
429    В следующем фрагменте кода демонстрируется порядок использования метода
430{@link android.content.ContentResolver#query ContentResolver.query()} (в качестве примера выступает
431поставщик пользовательского словаря): Клиентский запрос поставщика аналогичен SQL-запросу. В нем содержится
432набор столбцов, которые возвращаются, набор критериев выборки и порядок сортировки.
433</p>
434<p>
435    Набор столбцов, которые должен возвратить запрос, называется <strong>проекцией</strong>
436(переменная <code>mProjection</code>).
437</p>
438<p>
439    Выражение, которое задает строки для получения, состоит из предложения выбора
440и аргументов выбора. Предложение выбора представляет собой сочетание логических выражений,
441имен столбцов и значений (переменная <code>mSelectionClause</code>). Если вместо значения указать подставляемый параметр
442<code>?</code>, метод запроса извлекает значение из массива аргументов выбора (переменная
443<code>mSelectionArgs</code>).
444</p>
445<p>
446    В следующем фрагменте кода, если пользователь не указал слово, то для предложения выбора задается значение
447<code>null</code>, а запрос возвращает все слова, имеющиеся в поставщике. Если пользователь указал слово, то для предложения выбора задается значение
448<code>UserDictionary.Words.WORD + " = ?"</code>,
449а для первого элемента в массиве аргументов выбора задается введенное пользователем слово.
450</p>
451<pre class="prettyprint">
452/*
453 * This defines a one-element String array to contain the selection argument.
454 */
455String[] mSelectionArgs = {""};
456
457// Gets a word from the UI
458mSearchString = mSearchWord.getText().toString();
459
460// Remember to insert code here to check for invalid or malicious input.
461
462// If the word is the empty string, gets everything
463if (TextUtils.isEmpty(mSearchString)) {
464    // Setting the selection clause to null will return all words
465    mSelectionClause = null;
466    mSelectionArgs[0] = "";
467
468} else {
469    // Constructs a selection clause that matches the word that the user entered.
470    mSelectionClause = UserDictionary.Words.WORD + " = ?";
471
472    // Moves the user's input string to the selection arguments.
473    mSelectionArgs[0] = mSearchString;
474
475}
476
477// Does a query against the table and returns a Cursor object
478mCursor = getContentResolver().query(
479    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
480    mProjection,                       // The columns to return for each row
481    mSelectionClause                   // Either null, or the word the user entered
482    mSelectionArgs,                    // Either empty, or the string the user entered
483    mSortOrder);                       // The sort order for the returned rows
484
485// Some providers return null if an error occurs, others throw an exception
486if (null == mCursor) {
487    /*
488     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
489     * call android.util.Log.e() to log this error.
490     *
491     */
492// If the Cursor is empty, the provider found no matches
493} else if (mCursor.getCount() &lt; 1) {
494
495    /*
496     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
497     * an error. You may want to offer the user the option to insert a new row, or re-type the
498     * search term.
499     */
500
501} else {
502    // Insert code here to do something with the results
503
504}
505</pre>
506<p>
507    Этот запрос аналогичен следующей инструкции SQL:
508</p>
509<pre>
510SELECT _ID, word, locale FROM words WHERE word = &lt;userinput&gt; ORDER BY word ASC;
511</pre>
512<p>
513    В этой инструкции SQL вместо констант класса-контракта используются фактические имена столбцов.
514</p>
515<h4 id="Injection">Защита от ввода вредоносного кода</h4>
516<p>
517    Если данные, которыми управляет поставщик контента, находятся в базе данных SQL, то включение в необработанные инструкции
518SQL внешних ненадежных данных может привести к атаке путем внедрения кода SQL.
519</p>
520<p>
521    Рассмотрим следующее предложение выбора:
522</p>
523<pre>
524// Constructs a selection clause by concatenating the user's input to the column name
525String mSelectionClause =  "var = " + mUserInput;
526</pre>
527<p>
528    Если вы используете это предложение, вы разрешаете пользователю связать вашу инструкцию SQL с вредоносным кодом SQL.
529    Например, пользователь может ввести nothing; DROP TABLE *; для <code>mUserInput</code>, что
530приведет к созданию следующего предложения выбора: <code>var = nothing; DROP TABLE *;</code>. Поскольку
531предложение выбора выполняется в потоке как инструкция SQL, это может привести к тому, что поставщик удалит все
532таблицы в соответствующей базе данных SQLite (если только в поставщик не настроен на отслеживание попыток
533<a href="http://en.wikipedia.org/wiki/SQL_injection">внедрить вредоносный код SQL</a>).
534</p>
535<p>
536    Чтобы избежать этого, воспользуйтесь предложением выбора, в котором <code>?</code> выступает в качестве подставляемого
537параметра, а также отдельным массивом аргументов выбора. После этого ввод пользователя
538будет связан напрямую с запросом и не будет интерпретироваться как часть инструкции SQL.
539    Поскольку в этом случае введенный пользователем запрос не рассматривается как код SQL, то в него не удастся внедрить вредоносный код SQL. Вместо
540объединения, которое следует включить в пользовательский ввод, используйте следующее предложение выбора:
541</p>
542<pre>
543// Constructs a selection clause with a replaceable parameter
544String mSelectionClause =  "var = ?";
545</pre>
546<p>
547    Настройте массив аргументов выбора следующим образом:
548</p>
549<pre>
550// Defines an array to contain the selection arguments
551String[] selectionArgs = {""};
552</pre>
553<p>
554    Укажите значение для массива аргументов выбора:
555</p>
556<pre>
557// Sets the selection argument to the user's input
558selectionArgs[0] = mUserInput;
559</pre>
560<p>
561    Предложение выбора, в котором <code>?</code> используется в качестве подстановочного параметра, и массив
562аргументов выбора представляют собой предпочтительный способ указания выбора, даже если поставщик
563не использует базу данных SQL.
564</p>
565<!-- Displaying the results -->
566<h3 id="DisplayResults">Отображение результатов запроса</h3>
567<p>
568    Клиентский метод {@link android.content.ContentResolver#query ContentResolver.query()} всегда возвращает объект
569{@link android.database.Cursor}, содержащий столбцы, указанные в проекции
570запроса для строк, которые соответствуют критериям выборки в запросе. Объект
571{@link android.database.Cursor} предоставляет прямой доступ на чтение содержащихся в нем строк и
572столбцов. С помощью методов {@link android.database.Cursor} можно выполнить итерацию по строкам
573в результатах, определить тип данных для каждого столбца, получить данные из столбца, а также проверить другие свойства
574результатов. Некоторые реализации объекта {@link android.database.Cursor} автоматически обновляют
575объект при изменении данных в поставщике или запускают выполнение методов в объекте-наблюдателе
576при изменении объекта{@link android.database.Cursor}, либо выполняют и то, и другое.
577</p>
578<p class="note">
579    <strong>Примечание.</strong> Поставщик может ограничить доступ к столбцам на основе характера
580объекта, выполняющего запрос. Например, поставщик контактов ограничивает доступ адаптеров синхронизации к некоторым столбцам,
581поэтому он не возвращает их в операцию или службу.
582</p>
583<p>
584    Если строки, соответствующие критериям выборки, отсутствуют, поставщик
585возвращает объект{@link android.database.Cursor}, в котором для метода
586{@link android.database.Cursor#getCount Cursor.getCount()} указано значение «0» (пустой объект cursor).
587</p>
588<p>
589    При возникновении внутренней ошибки результаты запроса зависят от определенного поставщика. Поставщик может
590возвратить<code>null</code> или выдать {@link java.lang.Exception}.
591</p>
592<p>
593    Поскольку {@link android.database.Cursor} представляет собой «список» строк, то наилучшим способом отобразить содержимое объекта
594{@link android.database.Cursor} будет связать его с {@link android.widget.ListView}
595посредством {@link android.widget.SimpleCursorAdapter}.
596</p>
597<p>
598    Следующий фрагмент кода является продолжением предыдущего фрагмента. Он создает объект
599{@link android.widget.SimpleCursorAdapter}, содержащий объект{@link android.database.Cursor},
600который был получен в запросе, а затем определяет этот объект в качестве адаптера для
601{@link android.widget.ListView}:
602</p>
603<pre class="prettyprint">
604// Defines a list of columns to retrieve from the Cursor and load into an output row
605String[] mWordListColumns =
606{
607    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
608    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
609};
610
611// Defines a list of View IDs that will receive the Cursor columns for each row
612int[] mWordListItems = { R.id.dictWord, R.id.locale};
613
614// Creates a new SimpleCursorAdapter
615mCursorAdapter = new SimpleCursorAdapter(
616    getApplicationContext(),               // The application's Context object
617    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
618    mCursor,                               // The result from the query
619    mWordListColumns,                      // A string array of column names in the cursor
620    mWordListItems,                        // An integer array of view IDs in the row layout
621    0);                                    // Flags (usually none are needed)
622
623// Sets the adapter for the ListView
624mWordList.setAdapter(mCursorAdapter);
625</pre>
626<p class="note">
627    <strong>Примечание.</strong> Чтобы вернуть {@link android.widget.ListView} с объектом
628{@link android.database.Cursor}, объект cursor должен содержать столбец с именем <code>_ID</code>.
629    Поэтому показанный ранее запрос извлекает столбец<code>_ID</code> для таблицы
630words, даже если {@link android.widget.ListView} не отображает ее.
631    Данное ограничение также объясняет, почему в каждой таблице поставщика имеется столбец
632<code>_ID</code>.
633</p>
634
635        <!-- Getting data from query results -->
636<h3 id="GettingResults">Получение данных из результатов запроса</h3>
637<p>
638    Вместо того, чтобы просто отобразить результаты запроса, вы можете использовать их для выполнения других задач. Например,
639можно получить написание слов из пользовательского словаря, а затем выполнить их поиск в
640других поставщиках. Для этого выполните итерацию по строкам в объекте {@link android.database.Cursor}:
641</p>
642<pre class="prettyprint">
643
644// Determine the column index of the column named "word"
645int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
646
647/*
648 * Only executes if the cursor is valid. The User Dictionary Provider returns null if
649 * an internal error occurs. Other providers may throw an Exception instead of returning null.
650 */
651
652if (mCursor != null) {
653    /*
654     * Moves to the next row in the cursor. Before the first movement in the cursor, the
655     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
656     * exception.
657     */
658    while (mCursor.moveToNext()) {
659
660        // Gets the value from the column.
661        newWord = mCursor.getString(index);
662
663        // Insert code here to process the retrieved word.
664
665        ...
666
667        // end of while loop
668    }
669} else {
670
671    // Insert code here to report an error if the cursor is null or the provider threw an exception.
672}
673</pre>
674<p>
675    Реализации объекта {@link android.database.Cursor} содержат несколько методов get для
676получения из объекта различных типов данных. Например, в следующем фрагменте кода используется метод
677{@link android.database.Cursor#getString getString()}. В них также имеется метод
678{@link android.database.Cursor#getType getType()}, который возвращает значение, указывающее на тип
679данных в столбце.
680</p>
681
682
683    <!-- Requesting permissions -->
684<h2 id="Permissions">Разрешения поставщика контента</h2>
685<p>
686    Приложение поставщика может задавать разрешения, которые требуются другим приложениям для доступа к
687данным в поставщике. Такие разрешения гарантируют, что пользователь знает, к каким
688данным приложение будет пытаться получить доступ. На основе требований поставщика другие
689приложения запрашивают разрешения, которые требуются им для доступа к поставщику. Конечные пользователи видят
690запрошенные разрешения при установке приложения.
691</p>
692<p>
693    Если приложение поставщика не задает никаких разрешений, другие приложения не получают доступ к
694данным поставщика. Однако компонентам приложения поставщика
695всегда предоставлен полный доступ на чтение и запись, независимо от заданных разрешений.
696</p>
697<p>
698    Как уже было отмечено ранее, для получения данных из поставщика пользовательского словаря требуется разрешение
699<code>android.permission.READ_USER_DICTIONARY</code>.
700    В поставщике предусмотрено отдельное разрешение<code>android.permission.WRITE_USER_DICTIONARY</code>
701для вставки, обновления или удаления данных.
702</p>
703<p>
704    Чтобы получить разрешения, необходимые для доступа к поставщику, приложение запрашивает их с помощью элемента
705<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
706в файле манифеста. При установке менеджером пакетов Android приложения пользователю необходимо
707утвердить все разрешения, запрашиваемые приложением. В случае утверждения всех разрешений
708менеджер пакетов продолжает установку; если же пользователь отклоняет их, менеджер
709пакетов отменяет установку.
710</p>
711<p>
712    Для запроса доступа на чтение данных в поставщике пользовательского словаря используется
713следующий элемент
714<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>:
715</p>
716<pre>
717    &lt;uses-permission android:name="android.permission.READ_USER_DICTIONARY"&gt;
718</pre>
719<p>
720    Дополнительные сведения о влиянии разрешений на доступ к поставщику представлены в статье
721<a href="{@docRoot}guide/topics/security/security.html">Безопасность и разрешения</a>.
722</p>
723
724
725<!-- Inserting, Updating, and Deleting Data -->
726<h2 id="Modifications">Вставка, обновление и удаление данных</h2>
727<p>
728    Подобно тому, как вы получаете данные от поставщика, вы также можете можете использовать возможности взаимодействия между клиентом поставщика и объектом
729{@link android.content.ContentProvider} поставщика для изменения данных.
730    Можно вызвать метод объекта {@link android.content.ContentResolver}, указав аргументы,
731которые были переданы в соответствующий метод объекта {@link android.content.ContentProvider}. Поставщик и клиент поставщика
732автоматически обрабатывают взаимодействие между процессами и обеспечивают безопасность.
733</p>
734<h3 id="Inserting">Вставка данных</h3>
735<p>
736    Для вставки данных в поставщик вызовите
737метод
738{@link android.content.ContentResolver#insert ContentResolver.insert()}. Этот метод вставляет новую строку в поставщик и возвращает URI контента для этой строки.
739    В следующем фрагменте кода демонстрируется порядок вставки нового слова в поставщик пользовательского словаря:
740</p>
741<pre class="prettyprint">
742// Defines a new Uri object that receives the result of the insertion
743Uri mNewUri;
744
745...
746
747// Defines an object to contain the new values to insert
748ContentValues mNewValues = new ContentValues();
749
750/*
751 * Sets the values of each column and inserts the word. The arguments to the "put"
752 * method are "column name" and "value"
753 */
754mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
755mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
756mNewValues.put(UserDictionary.Words.WORD, "insert");
757mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
758
759mNewUri = getContentResolver().insert(
760    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
761    mNewValues                          // the values to insert
762);
763</pre>
764<p>
765    Данные для новой строки поступают в один объект {@link android.content.ContentValues},
766который аналогичен объекту cursor с одной строкой. Столбцы в этом объекте необязательно
767должны содержать данные такого же типа, и если вы вообще не собираетесь указывать значение, вы можете задать для столбца значение
768<code>null</code> с помощью метода {@link android.content.ContentValues#putNull ContentValues.putNull()}.
769</p>
770<p>
771    Код в представленном фрагменте не добавляет столбец <code>_ID</code>, поскольку этот столбец сохраняется
772автоматически. Поставщик присваивает уникальное значение <code>_ID</code> каждой
773добавляемой строке. Обычно поставщики используют это значение в качестве основного ключа таблицы.
774</p>
775<p>
776    URI контента, возвращенный в элементе <code>newUri</code>, служит для идентификации новой добавленной строки
777в следующем формате:
778</p>
779<pre>
780content://user_dictionary/words/&lt;id_value&gt;
781</pre>
782<p>
783    <code>&lt;id_value&gt;</code> — это содержимое столбца <code>_ID</code> для новой строки.
784    Большинство поставщиков автоматически определяют эту форму URI контента, а затем
785выполняют запрошенную операцию с требуемой строкой.
786</p>
787<p>
788    Чтобы получить значение <code>_ID</code> из возвращенного объекта {@link android.net.Uri}, вызовите метод
789{@link android.content.ContentUris#parseId ContentUris.parseId()}.
790</p>
791<h3 id="Updating">Обновление данных</h3>
792<p>
793    Чтобы обновить строку, используйте объект {@link android.content.ContentValues} с обновленными
794значениями (точно так же, как вы это делаете при вставке) и критериями выборки (так же, как и с запросом).
795    Используемый вами клиентский метод называется
796{@link android.content.ContentResolver#update ContentResolver.update()}. Вам не нужно добавлять значения в объект
797{@link android.content.ContentValues} для обновляемых столбцов. Чтобы очистить содержимое столбца, задайте значение
798<code>null</code>.
799</p>
800<p>
801    Следующий фрагмент кода служит для изменения языка во всех строках, где в качестве языка указано en, на
802<code>null</code>. Возвращаемое значение представляет собой количество строк, которые были обновлены:
803</p>
804<pre>
805// Defines an object to contain the updated values
806ContentValues mUpdateValues = new ContentValues();
807
808// Defines selection criteria for the rows you want to update
809String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
810String[] mSelectionArgs = {"en_%"};
811
812// Defines a variable to contain the number of updated rows
813int mRowsUpdated = 0;
814
815...
816
817/*
818 * Sets the updated value and updates the selected words.
819 */
820mUpdateValues.putNull(UserDictionary.Words.LOCALE);
821
822mRowsUpdated = getContentResolver().update(
823    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
824    mUpdateValues                       // the columns to update
825    mSelectionClause                    // the column to select on
826    mSelectionArgs                      // the value to compare to
827);
828</pre>
829<p>
830    Также следует проверить пользовательский ввод при вызове метода
831{@link android.content.ContentResolver#update ContentResolver.update()}. Дополнительные сведения об этом
832представлены в разделе <a href="#Injection">Защита от ввода вредоносного кода</a>.
833</p>
834<h3 id="Deleting">Удаление данных</h3>
835<p>
836    Удаление данных аналогично получению данных строки: необходимо указать критерии выборки для строк,
837которые требуется удалить, после чего клиентский метод возвратит количество удаленных строк.
838    Ниже представлен фрагмент кода для удаления строк с идентификатором appid user. Метод возвращает
839количество удаленных строк.
840</p>
841<pre>
842
843// Defines selection criteria for the rows you want to delete
844String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
845String[] mSelectionArgs = {"user"};
846
847// Defines a variable to contain the number of rows deleted
848int mRowsDeleted = 0;
849
850...
851
852// Deletes the words that match the selection criteria
853mRowsDeleted = getContentResolver().delete(
854    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
855    mSelectionClause                    // the column to select on
856    mSelectionArgs                      // the value to compare to
857);
858</pre>
859<p>
860    Также следует проверить пользовательский ввод при вызове метода
861{@link android.content.ContentResolver#delete ContentResolver.delete()}. Дополнительные сведения об этом
862представлены в разделе <a href="#Injection">Защита от ввода вредоносного кода</a>.
863</p>
864<!-- Provider Data Types -->
865<h2 id="DataTypes">Типы поставщиков данных</h2>
866<p>
867    Поставщики контента могут предоставлять различные тип данных. Поставщик пользовательского словаря предоставляет только
868текст, но также может предоставлять следующие форматы:
869</p>
870    <ul>
871        <li>
872            целое число;
873        </li>
874        <li>
875            длинное целое число (long);
876        </li>
877        <li>
878            число с плавающей запятой;
879        </li>
880        <li>
881            длинное число с плавающей запятой (double).
882        </li>
883    </ul>
884<p>
885    Другим типом данных, предлагаемых поставщиком, является большой двоичный объект (BLOB), реализованный как
88664-разрядный массив. Чтобы просмотреть доступные типы данных, обратитесь к методам get класса
887{@link android.database.Cursor}.
888</p>
889<p>
890    Тип данных для каждого столбца в поставщике обычно указывается в документации к поставщику.
891    Типы данных для поставщика пользовательского словаря указаны в справочной документации
892для класса-контракта {@link android.provider.UserDictionary.Words} (дополнительные сведения о классах-контрактах представлены в разделе
893<a href="#ContractClasses">Классы-контракты</a>).
894    Также определить тип данных можно путем вызова метода {@link android.database.Cursor#getType
895Cursor.getType()}.
896</p>
897<p>
898    Поставщики также хранят информацию о типе данных MIME для каждого определяемого ими URI контента. Эту информацию
899можно использовать для определения того, может ли ваше приложение обрабатывать предлагаемые
900поставщиком данные, а также для выбора типа обработки на основе типа MIME. Информация о типе
901MIME обычно требуется при работе с поставщиком, который содержит
902сложные структуры данных или файлы. Например, в таблице{@link android.provider.ContactsContract.Data}
903в поставщике контактов используются типы MIME для отметки типа данных контакта, которые хранятся в каждой
904строке. Чтобы получить тип MIME, соответствующий URI контента, вызовите метод
905{@link android.content.ContentResolver#getType ContentResolver.getType()}.
906</p>
907<p>
908    Синтаксис стандартных и настраиваемых типов MIME описан в
909<a href="#MIMETypeReference">справке по типам MIME</a>.
910</p>
911
912
913<!-- Alternative Forms of Provider Access -->
914<h2 id="AltForms">Альтернативные формы доступа к поставщику</h2>
915<p>
916    При разработке приложения следует учитывать три альтернативных формы доступа к поставщику:
917</p>
918<ul>
919    <li>
920        <a href="#Batch">Пакетный доступ:</a> можно создать пакет вызовов доступа с использованием методов в классе
921{@link android.content.ContentProviderOperation}, а затем применить их с помощью метода
922{@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}.
923    </li>
924    <li>
925        Асинхронные запросы: Запросы следует выполнять в отдельном потоке. Одним из способов реализовать это является использование объекта
926{@link android.content.CursorLoader}. Примеры этого представлены в
927статье
928<a href="{@docRoot}guide/components/loaders.html">Загрузчики</a>.
929    </li>
930    <li>
931        <a href="#Intents">Доступ к данным с помощью намерений:</a> Несмотря на то, что намерение
932невозможно отправить напрямую в поставщик, вы можете отправить запрос в приложение поставщика, в котором обычно
933имеется больше возможностей для изменения данных поставщика.
934    </li>
935</ul>
936<p>
937    Пакетный доступ и изменение с помощью намерений описаны в следующих разделах.
938</p>
939<h3 id="Batch">Пакетный доступ</h3>
940<p>
941    Пакетный доступ к поставщику полезно использовать в случаях, когда необходимо вставить большое количество строк, или для вставки
942строк в несколько таблиц в рамках одного вызова метода, а также в общих случаях для выполнения ряда
943операций на границах процессов в виде транзакции (атомарной операции).
944</p>
945<p>
946    Для доступа к поставщику в «пакетном режиме» необходимо создать массив объектов
947{@link android.content.ContentProviderOperation}, а затем
948отправить их в поставщик контента с помощью метода
949{@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. В этот метод необходимо передать
950<em>центр</em> поставщика контента, а не определенный URI контента.
951Это позволит каждому объекту {@link android.content.ContentProviderOperation} в массиве взаимодействовать
952с разными таблицами. Метод {@link android.content.ContentResolver#applyBatch
953ContentResolver.applyBatch()} возвращает массив результатов.
954</p>
955<p>
956    В описании класса-контракта {@link android.provider.ContactsContract.RawContacts}
957также представлен фрагмент кода, в котором демонстрируется вставка в пакетном режиме. В исходном файле <code>ContactAdder.java</code> примера приложения
958<a href="{@docRoot}resources/samples/ContactManager/index.html">Диспетчер контактов</a>
959имеется пример пакетного
960доступа.
961</p>
962<div class="sidebox-wrapper">
963<div class="sidebox">
964<h2>Отображение данных с помощью вспомогательного приложения</h2>
965<p>
966    Если вашему приложению <em>не предоставлены</em> разрешения, вы по-прежнему можете воспользоваться
967намерением для отображения данных в другом приложении. Например, приложение «Календарь» принимает намерение
968{@link android.content.Intent#ACTION_VIEW}, которое позволяет отобразить определенную дату или событие.
969    Благодаря этому информацию календаря можно отображать без необходимости создавать собственный пользовательский интерфейс.
970Дополнительные сведения об этой функции представлены в статье
971<a href="{@docRoot}guide/topics/providers/calendar-provider.html">Поставщик календаря</a>.
972</p>
973<p>
974    Приложение, в которое вы отправляете намерение, не обязательно
975должно быть связано с поставщиком. Например,
976в поставщике контактов можно создать форму контакта, а затем отправить намерение {@link android.content.Intent#ACTION_VIEW},
977содержащее URI контента для изображения контакта, в средство просмотра изображений.
978</p>
979</div>
980</div>
981<h3 id="Intents">Доступ к данным с помощью намерений</h3>
982<p>
983    Намерения позволяют в обход получать доступ к поставщику контента. Вы можете разрешить пользователям доступ к
984данным в поставщике даже в том случае, если у приложения отсутствуют разрешения на доступ, либо путем
985получения результирующего намерения от приложения, у которого имеются необходимые разрешения, либо путем активации
986приложения, у которого имеются разрешения и которое разрешает пользователю работать с ним.
987</p>
988<h4>Получение доступа с временными разрешениями</h4>
989<p>
990    Вы можете получить доступ к данным в поставщике контента даже тогда, когда у вас нет необходимых разрешений на доступ
991, путем отправки намерения в приложение, у которого есть такие разрешения, и получения
992результирующего намерения, которое содержит разрешения URI.
993    Эти разрешения для определенного URI контента действуют до тех пор, пока не будет завершена операция, получившая
994их. Приложение, у которой имеются бессрочные разрешения, предоставляет временные
995разрешения путем задания соответствующего флага в результирующем намерении:
996</p>
997<ul>
998    <li>
999        <strong>Разрешение на чтение:</strong>
1000{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}
1001    </li>
1002    <li>
1003        <strong>Разрешение на запись:</strong>
1004{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}
1005    </li>
1006</ul>
1007<p class="note">
1008    <strong>Примечание.</strong> Эти флаги не предоставляют общий доступ на чтение или запись поставщику,
1009центр которого указан в URI контента. Доступ предоставляется только самому URI.
1010</p>
1011<p>
1012    Поставщик определяет разрешения URI для URI контента в своем манифесте с помощью атрибута
1013<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code>
1014элемента
1015<code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>,
1016а также
1017с помощью дочернего элемента
1018<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">&lt;grant-uri-permission&gt;</a></code>
1019элемента
1020<code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>. Дополнительные сведения о механизме разрешений URI представлены в статье
1021<a href="{@docRoot}guide/topics/security/security.html">Безопасность и разрешения</a> в разделе
1022Разрешения URI.
1023</p>
1024<p>
1025    Например, можно получить данные о контакте из поставщика контактов, даже если у вас нет разрешения
1026{@link android.Manifest.permission#READ_CONTACTS}. Возможно, это потребуется реализовать
1027в приложении, которое отправляет электронные поздравления контакту в день его рождения. Вместо запроса
1028{@link android.Manifest.permission#READ_CONTACTS}, когда вы получаете доступ ко всем контактам пользователя
1029и всей информации о них, можно предоставить пользователю возможность указать,
1030какие контакты используются вашим приложением. Для этого воспользуйтесь указанным ниже процессом.
1031</p>
1032<ol>
1033    <li>
1034        Ваше приложение отправляет намерение, содержащее действие
1035{@link android.content.Intent#ACTION_PICK} и тип MIME
1036{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} контактов, используя для этого метод
1037{@link android.app.Activity#startActivityForResult
1038startActivityForResult()}.
1039    </li>
1040    <li>
1041        Поскольку это намерение соответствует условиям отбора намерений для операции выбора
1042приложения «Контакты», эта операция переходит на передний план.
1043    </li>
1044    <li>
1045        В операции выбора пользователь выбирает
1046контакт для обновления. Когда это происходит, операция выбора вызывает метод
1047{@link android.app.Activity#setResult setResult(resultcode, intent)}
1048для создания намерения, которое будет передано обратно в ваше приложение. Намерение содержит URI контента
1049выбранного пользователем контакта, а также флаги
1050{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} дополнительных данных. Эти флаги предоставляют вашему приложению разрешение URI
1051на чтение данных контакта, на который указывает
1052URI контента. Затем операция выбора вызывает метод{@link android.app.Activity#finish()},
1053чтобы вернуть управление вашему приложению.
1054    </li>
1055    <li>
1056        Ваша операция возвращается на передний план, а система вызывает метод
1057{@link android.app.Activity#onActivityResult onActivityResult()}
1058вашей операции. Этот метод получает результирующее намерение, созданное операцией выбора в
1059приложении «Контакты».
1060    </li>
1061    <li>
1062        С помощью URI контента из результирующего намерения можно выполнить чтение данных контакта
1063из поставщика контактов, даже если вы не запрашивали у поставщика постоянный доступ на чтение
1064в своем манифесте. Можно получить информацию о дне рождения контакта
1065или сведения о его адресе эл. почты, а затем отправить контакту электронное поздравление.
1066    </li>
1067</ol>
1068<h4>Использование другого приложения</h4>
1069<p>
1070    Простой способ разрешить пользователю изменять данные, на доступ к которым у вас нет доступа
1071— это активировать приложение, у которого есть такие разрешения, а затем предоставить пользователю возможность выполнять необходимые действия в этом приложении.
1072</p>
1073<p>
1074    Например, приложение «Календарь» принимает намерения
1075{@link android.content.Intent#ACTION_INSERT}, с помощью которого можно активировать
1076пользовательский интерфейс приложения для вставки. Вы можете передать в это намерение дополнительные данные, которые приложение
1077использует для заполнения полей в пользовательском интерфейсе. Поскольку синтаксис повторяющихся событий довольно сложный, то события предпочтительно
1078вставлять в поставщик календаря путем активации приложения «Календарь» с помощью действия
1079{@link android.content.Intent#ACTION_INSERT} и последующего предоставления пользователю возможности самому вставить событие в этом приложении.
1080</p>
1081<!-- Contract Classes -->
1082<h2 id="ContractClasses">Классы-контракты</h2>
1083<p>
1084    Класс-контракт определяет константы, которые обеспечивают для приложений возможность работать с URI контента, именами
1085столбцов, операциями намерения и другими функциями поставщика контента. Классы-контракты не
1086включены в поставщик; разработчику поставщика следует определить их и сделать
1087их доступными для других разработчиков. Многие из поставщиков, включенные в платформу Android,
1088содержат соответствующие классы-контракты в пакете {@link android.provider}.
1089</p>
1090<p>
1091    Например, в поставщике пользовательского календаря имеется класс-контракт
1092{@link android.provider.UserDictionary}, содержащий константы URI контента и имен столбцов. URI
1093контента для таблицы words определен в константе
1094{@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}.
1095    В классе {@link android.provider.UserDictionary.Words} также имеются константы имен столбцов,
1096которые используются в фрагментах кода примера приложения, представленных в этой статье. Например, проекцию запроса
1097можно определить следующим образом:
1098</p>
1099<pre>
1100String[] mProjection =
1101{
1102    UserDictionary.Words._ID,
1103    UserDictionary.Words.WORD,
1104    UserDictionary.Words.LOCALE
1105};
1106</pre>
1107<p>
1108    Другим классом-контрактом является класс {@link android.provider.ContactsContract} для поставщика контактов.
1109    В справочной документации к этому классу представлены фрагменты кода примера приложения. Один из его подклассов,
1110{@link android.provider.ContactsContract.Intents.Insert}, представляет собой класс-контракт,
1111который содержит константы для намерений и их данных.
1112</p>
1113
1114
1115<!-- MIME Type Reference -->
1116<h2 id="MIMETypeReference">Справка по типам MIME</h2>
1117<p>
1118    Поставщики контента могут возвращать как стандартные типы мультимедиа MIME, так и строки с настраиваемым типом MIME, либо оба этих типа.
1119</p>
1120<p>
1121    Типы MIME имеют следующий формат:
1122</p>
1123<pre>
1124<em>type</em>/<em>subtype</em>
1125</pre>
1126<p>
1127    Например, хорошо известный тип MIME <code>text/html</code> имеет тип <code>text</code> и подтип
1128<code>html</code>. Если поставщик возвращает этот тип URI, это означает, что
1129строка запроса, в которой используется этот URI, возвратит текста с тегами HTML.
1130</p>
1131<p>
1132    Строки с настраиваемым типом MIME, которые также называются типами MIME поставщика, имеют более сложные значения
1133<em>типов</em> и <em>подтипов</em>. Значение <em>типа</em> всегда следующее:
1134</p>
1135<pre>
1136vnd.android.cursor.<strong>dir</strong>
1137</pre>
1138<p>
1139    для нескольких строк, или
1140</p>
1141<pre>
1142vnd.android.cursor.<strong>item</strong>
1143</pre>
1144<p>
1145    для одной строки.
1146</p>
1147<p>
1148    <em>Подтип</em> зависит от поставщика. Встроенные поставщики Android обычно содержат простой
1149подтип. Например, когда приложение «Контакты» создает строку для номера телефона,
1150оно задает следующий тип MIME в этой строке:
1151</p>
1152<pre>
1153vnd.android.cursor.item/phone_v2
1154</pre>
1155<p>
1156    Обратите внимание, что значение подтипа просто <code>phone_v2</code>.
1157</p>
1158<p>
1159    Разработчики поставщиков могут создавать свои собственные шаблоны подтипов на основе
1160центра и названий таблиц поставщика. Например, рассмотрим поставщик, который содержит расписание движения поездов.
1161    Центром поставщика является <code>com.example.trains</code>, в котором содержатся таблицы
1162Line1, Line2 и Line3. В ответ на следующий URI контента
1163</p>
1164<p>
1165<pre>
1166content://com.example.trains/Line1
1167</pre>
1168<p>
1169    для таблицы Line1 поставщик возвращает следующий тип MIME
1170</p>
1171<pre>
1172vnd.android.cursor.<strong>dir</strong>/vnd.example.line1
1173</pre>
1174<p>
1175     В ответ на следующий URI контента
1176</p>
1177<pre>
1178content://com.example.trains/Line2/5
1179</pre>
1180<p>
1181    для строки 5 в таблице Line2 поставщик возвращает следующий тип MIME
1182</p>
1183<pre>
1184vnd.android.cursor.<strong>item</strong>/vnd.example.line2
1185</pre>
1186<p>
1187    В большинстве поставщиков контента определены константы класса-контракта для используемых в них типов MIME. Например, класс-контракт
1188{@link android.provider.ContactsContract.RawContacts}
1189поставщика контактов определяет константу
1190{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} для типа MIME одной
1191строки необработанного контакта.
1192</p>
1193<p>
1194    URI контента для единичных строк описываются в разделе
1195<a href="#ContentURIs">URI контента</a>.
1196</p>
1197