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"><uses-permission></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() < 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 = <userinput> 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"><uses-permission></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"><uses-permission></a></code>: 715</p> 716<pre> 717 <uses-permission android:name="android.permission.READ_USER_DICTIONARY"> 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/<id_value> 781</pre> 782<p> 783 <code><id_value></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"><provider></a></code>, 1016а также 1017с помощью дочернего элемента 1018<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></code> 1019элемента 1020<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></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