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 游標 (使用者)</a> 98 </li> 99 <li> 100 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html"> 101 游標 (電話)</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 內容供應程式可管理中央資料存放庫的存取權。供應程式是 Android 應用程式的一部分,通常可提供本身的 UI 方便使用者處理資料。 123 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">字詞</th> 158 <th style="width:20%" align="center" scope="col">應用程式 ID</th> 159 <th style="width:20%" align="center" scope="col">頻率</th> 160 <th style="width:20%" align="center" scope="col">地區</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> 欄。以這個供應程式為例,<code>_ID</code> 欄的用途與供應程式自動維護的「主索引鍵」欄相同。 204 205 206</p> 207<p class="note"> 208 <strong>注意:</strong>供應程式不需具備主索引鍵,也不必採用 <code>_ID</code> 做為主索引鍵欄的名稱 (如果有此欄的話)。 209不過,如果您想將供應程式的資料繫結至 {@link android.widget.ListView},就必須將其中一個資料欄命名為 <code>_ID</code>。 210 211如要進一步瞭解這項規定,請參閱<a href="#DisplayResults">顯示查詢結果</a>。 212 213</p> 214<h3 id="ClientProvider">存取供應程式</h3> 215<p> 216 應用程式會透過 {@link android.content.ContentResolver} 用戶端物件存取內容供應程式的資料。 217此物件內含的方法可呼叫供應程式物件 ({@link android.content.ContentProvider} 子類別的其中一項執行個體) 中的同名方法。 218 219 220{@link android.content.ContentResolver} 方法可提供永久儲存空間的「CRUD」(建立、擷取、更新、刪除) 基本功能。 221 222</p> 223<p> 224 用戶端應用程式處理程序中的 {@link android.content.ContentResolver} 物件以及擁有供應程式的應用程式中的 {@link android.content.ContentProvider} 物件,會自動處理程序間通訊。 225{@link android.content.ContentProvider} 還可當作其資料存放庫與外部資料表格之間的抽象層。 226 227 228 229</p> 230<p class="note"> 231 <strong>注意:</strong>如要存取供應程式,您的應用程式通常必須透過本身的宣示說明檔案要求特定權限。 232如需詳細資料,請參閱<a href="#Permissions">內容供應程式權限</a>。 233 234</p> 235<p> 236 例如,如要從使用者字典供應程式取得一份列出字詞及其地區的清單,請呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 237 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> 會對應至供應程式中名為的「table_name」<em></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 (沒有任何相對應的關鍵字/參數。選取引數會取代選取子句中的 288<code>?</code> 預留位置。) 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> 會指定資料列在傳回的 {@link android.database.Cursor} 中的顯示順序。 296 297 </td> 298 </tr> 299</table> 300<h3 id="ContentURIs">內容 URI</h3> 301<p> 302 <strong>內容 URI</strong> 是指用於識別供應程式資料的 URI,內容 URI 包括整個供應程式的符號名稱 (亦即供應程式的<strong>授權</strong>),以及指向表格的名稱 (亦即<strong>路徑</strong>)。 303 304當您呼叫用戶端方法來存取供應程式中的表格時,該表格的內容 URI 即為其中一個引數。 305 306 307</p> 308<p> 309 在上方程式碼中, 310{@link android.provider.UserDictionary.Words#CONTENT_URI} 常數包含使用者字典表格「字詞」的內容 URI。 311{@link android.content.ContentResolver} 物件會剖析該 URI 的授權,然後比較授權和已知供應程式的系統表格,藉此「解析」供應程式。 312 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 會如下所示: 323</p> 324<pre> 325content://user_dictionary/words 326</pre> 327<p> 328 其中的 <code>user_dictionary</code> 字串代表供應程式的授權,而 <code>words</code> 字串則是表格的路徑。 329字串 <code>content://</code> (<strong>配置</strong>) 一律會顯示,而起會將此項目識別為內容 URI。 330 331 332</p> 333<p> 334 許多供應程式都可讓您存取表格中的單一資料列,方法是在 URI 後方附加 ID 值。例如,如要從使用者字典擷取 <code>_ID</code> 為 <code>4</code> 的資料列,請使用以下內容 URI: 335 336 337</p> 338<pre> 339Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4); 340</pre> 341<p> 342 在更新或刪除您擷取的一組資料列時,通常需要使用 ID 值。 343 344</p> 345<p class="note"> 346 <strong>注意:</strong>{@link android.net.Uri} 和 {@link android.net.Uri.Builder} 類別包含可用於從字串建構格式正確之 URI 物件的簡便方法。 347而 {@link android.content.ContentUris} 則包含可用於將 ID 值附加至 URI 的簡便方法。上方程式碼片段是使用 {@link android.content.ContentUris#withAppendedId 348withAppendedId()} 將 ID 附加至 UserDictionary 內容 URI。 349 350 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 為了避免造成混淆,本節中的程式碼片段是透過「UI 執行緒」呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 362不過在實際程式碼中,您需要透過個別執行緒以非同步方式進行查詢。 363您可以使用 {@link android.content.CursorLoader} 類別 (詳情請參閱<a href="{@docRoot}guide/components/loaders.html">載入器</a>指南) 來完成這項作業。 364 365 366此外,本節中的程式碼都只是程式碼片段,無法呈現完整的應用程式。 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您無法在執行階段要求此權限;您必須使用 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素和供應程式所定義的確切權限名稱,在宣示說明中指明您需要此權限。 384 385 386 387在宣示說明中指定此元素後,即可為您的應用程式「要求」這項權限。 388當使用者安裝您應用程式時,他們會間接核准此要求。 389 390</p> 391<p> 392 如要找出所使用供應程式的確切讀取權限名稱,以及該供應程式使用的其他存取權限名稱,請查閱供應程式的說明文件。 393 394 395</p> 396<p> 397 如要進一步瞭解用於存取供應程式的權限角色,請參閱<a href="#Permissions">內容供應程式權限</a>。 398 399</p> 400<p> 401 使用者字典供應程式會在本身的宣示說明檔案中定義 <code>android.permission.READ_USER_DICTIONARY</code> 權限,因此想讀取該供應程式的應用程式都必須要求此權限。 402 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 下一個程式碼片段以使用者字典供應程式為例,示範 {@link android.content.ContentResolver#query ContentResolver.query()} 的使用方式。 430 431供應程式用戶端查詢類似於 SQL 查詢,包含一組要傳回的資料欄、一組選取條件以及一個排序順序。 432 433</p> 434<p> 435 查詢需傳回的一組資料欄稱為「投影」<strong></strong>(即 <code>mProjection</code> 變數)。 436 437</p> 438<p> 439 指定要擷取的資料列的運算式會分為選取子句和選取引數。 440選取子句包含邏輯運算式和布林運算式、欄名稱和值 (即 <code>mSelectionClause</code> 變數)。 441如果您指定可替換的參數 <code>?</code>,而不是某個值,則查詢方法會從選取引數陣列 (即 <code>mSelectionArgs</code> 變數) 擷取相關值。 442 443 444</p> 445<p> 446 在下方程式碼片段中,如果使用者未輸入任何字詞,選取子句會設定為 <code>null</code>,此時查詢會傳回供應程式中的所有字詞。 447如果使用者輸入了某個字詞,則選取子句會設定為 <code>UserDictionary.Words.WORD + " = ?"</code>,而選取引數陣列的第一個元素會設為使用者所輸入的字詞。 448 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 資料庫中,在原始 SQL 陳述式中納入不受信任的外部資料可能會導致 SQL 遭植入惡意程式碼。 518 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 例如,使用者可針對 <code>mUserInput</code> 輸入「nothing; DROP TABLE *;」,以產生 <code>var = nothing; DROP TABLE *;</code> 選取子句。 530由於系統會將選取子句視為 SQL 陳述式,因此這種選取子句可能會導致供應程式清除底層 SQLite 資料庫中的所有表格 (除非您設定供應程式捕捉 <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL 植入</a>嘗試)。 531 532 533 534</p> 535<p> 536 為了避免發生這個問題,請使用包含 <code>?</code> 可替換參數和一系列選取引數的選取子句。 537當您執行此動作時,使用者輸入會直接繫結到查詢,而不是被解譯為 SQL 陳述式的一部分。 538 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> 可替換參數和一系列選取引數的選取子句,即使供應程式並非以 SQL 資料庫為基礎亦然。 562 563 564</p> 565<!-- Displaying the results --> 566<h3 id="DisplayResults">顯示查詢結果</h3> 567<p> 568 {@link android.content.ContentResolver#query ContentResolver.query()} 用戶端方法一律會針對符合查詢選取條件的資料列,傳回內含查詢的投影所指定資料欄的 {@link android.database.Cursor}。 569 570{@link android.database.Cursor} 物件會針對本身包含的資料列和資料欄提供隨機讀取存取權。 571 572您可以使用 {@link android.database.Cursor} 方法逐一查看結果中的資料列、決定每個資料欄的資料類型、匯出資料欄的資料,以及檢查結果的其他屬性。 573 574實作某些 {@link android.database.Cursor} 方法可在供應程式的資料變更時自動更新相關物件,或在 {@link android.database.Cursor} 變更時觸發觀察器物件中的方法,或是同時進行以上兩者。 575 576 577</p> 578<p class="note"> 579 <strong>注意:</strong>供應程式可能會根據建立查詢物件的屬性限制資料欄的存取權。 580例如,內容供應程式會限制同步配接器存取部分資料欄,避免將這些資料欄傳回 Activity 或服務。 581 582</p> 583<p> 584 如果沒有任何資料欄符合選取條件,則供應程式會傳回 {@link android.database.Cursor#getCount Cursor.getCount()} 為 0 的 585{@link android.database.Cursor} 物件 (即沒有任何內容的游標)。 586 587</p> 588<p> 589 如果發生內部錯誤,查詢結果將取決於供應程式的決定。供應程式可能會選擇傳回 <code>null</code>,或是擲回 {@link java.lang.Exception}。 590 591</p> 592<p> 593 由於 {@link android.database.Cursor} 是一份資料欄「清單」,因此要顯示 {@link android.database.Cursor} 的內容,最佳做法是透過 {@link android.widget.SimpleCursorAdapter} 將其連結至 {@link android.widget.ListView}。 594 595 596</p> 597<p> 598 以下程式碼片段是上一個程式碼片段的延伸。它會建立內含查詢所擷取 {@link android.database.Cursor} 的 599{@link android.widget.SimpleCursorAdapter} 物件,並將該物件設定為 {@link android.widget.ListView} 的配接器: 600 601 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.database.Cursor} 的{@link android.widget.ListView},您必須為游標加入名為 <code>_ID</code> 的資料欄。 628 629 因為這樣,上述查詢會擷取「字詞」表格的 <code>_ID</code> 欄,即使 {@link android.widget.ListView} 未顯示該資料欄亦然。 630 631 這項限制也是為何大多數供應程式會針對本身的所有表格設定 <code>_ID</code> 欄的原因。 632 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例如,上方程式碼片段使用了 {@link android.database.Cursor#getString getString()}。 677此外,這種實作方法還包括 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 如上所述,使用者字典供應程式要求其他應用程式需取得 <code>android.permission.READ_USER_DICTIONARY</code> 權限,才能擷取其中的資料。 699 700 而該供應程式還個別針對插入、更新或刪除資料用途,指定了不同的 <code>android.permission.WRITE_USER_DICTIONARY</code> 權限。 701 702</p> 703<p> 704 如要取得存取供應程式時所需的權限,應用程式可利用 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素在本身的宣示說明檔案中要求相關權限。 705 706當 Android 套件管理員安裝應用程式時,使用者必須授予該應用程式要求的所有權限。 707使用者授予所有權限後,套件管理員才能繼續進行安裝作業;如果使用者不授予權限,則套件管理員就會取消安裝。 708 709 710</p> 711<p> 712 以下的 713<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 714 元素會要求使用者字典供應程式的讀取存取權: 715</p> 716<pre> 717 <uses-permission android:name="android.permission.READ_USER_DICTIONARY"> 718</pre> 719<p> 720 如要進一步瞭解權限對供應程式存取的影響,請參閱<a href="{@docRoot}guide/topics/security/security.html">安全性和權限</a>指南。 721 722</p> 723 724 725<!-- Inserting, Updating, and Deleting Data --> 726<h2 id="Modifications">插入、更新及刪除資料</h2> 727<p> 728 透過從供應程式擷取資料的相同方式,您也可以利用供應程式用戶端與供應程式的 {@link android.content.ContentProvider} 之間的互動來修改資料。 729 730 如要這麼做,請呼叫其中引數已傳送到相對應 {@link android.content.ContentProvider} 方法的 {@link android.content.ContentResolver} 方法。 731供應程式和供應程式用戶端會自動處理安全性和處理程序間通訊。 732 733</p> 734<h3 id="Inserting">插入資料</h3> 735<p> 736 如要在供應程式中插入資料,請呼叫 737{@link android.content.ContentResolver#insert ContentResolver.insert()} 方法。 738這個方法會在供應程式中插入新的資料列,並傳回該列的內容 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您不必為這個物件中的資料欄指定相同的資料類型,而且如果您不想指定任何值,可以將資料欄設定為 <code>null</code> 以使用 {@link android.content.ContentValues#putNull ContentValues.putNull()}。 767 768 769</p> 770<p> 771 該程式碼片段並不會新增 <code>_ID</code> 欄,這是因為系統自動會維護該欄。 772供應程式會將不重複值 <code>_ID</code> 指派給新增的所有資料列。 773供應程式通常會採用該值做為表格的主索引鍵。 774</p> 775<p> 776 <code>newUri</code> 以下方格式傳回的內容 URI 可用於識別新增的資料列: 777 778</p> 779<pre> 780content://user_dictionary/words/<id_value> 781</pre> 782<p> 783 <code><id_value></code> 是 ID 為 <code>_ID</code> 的資料列內容。 784 大多數供應程式可自動偵測這種格式的內容 URI,並據以針對指定的資料列執行特定作業。 785 786</p> 787<p> 788 如要從傳回的 {@link android.net.Uri} 取得 <code>_ID</code> 的值,請呼叫 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()}。您只需針對要更新的資料欄,將相關值加到 {@link android.content.ContentValues} 物件即可。 797如果您想清除資料欄的內容,請將值設定為 <code>null</code>。 798 799</p> 800<p> 801 以下程式碼片段會針對地區代碼含有「en」字詞的所有資料列,將其地區代碼變更為 <code>null</code>。 802系統會傳回已更新的資料列數量: 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 建議您在呼叫 {@link android.content.ContentResolver#update ContentResolver.update()} 時對使用者輸入進行檢測。 831如需詳細資訊,請參閱<a href="#Injection">防範惡意輸入</a>。 832 833</p> 834<h3 id="Deleting">刪除資料</h3> 835<p> 836 刪除資料列的方法與擷取資料列資料類似:您必須為想刪除的資料列指定選取條件,用戶端方法最後會傳回已刪除的資料列數量。 837 838 以下程式碼片段可刪除應用程式 ID 為「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 建議您在呼叫 {@link android.content.ContentResolver#delete ContentResolver.delete()} 時對使用者輸入進行檢測。 861如需詳細資訊,請參閱<a href="#Injection">防範惡意輸入</a>。 862 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),這種資料會實作成 64 KB 位元組陣列。 886如果想瞭解可用的資料類型,請查閱 {@link android.database.Cursor} 類別的「get」方法。 887 888</p> 889<p> 890 供應程式的說明文件通常會列出其中每個資料欄的資料類型。 891 使用者字典供應程式的資料類型列在其合約類別 {@link android.provider.UserDictionary.Words} 的參考文件 (如需合約類別的相關資訊,請參閱<a href="#ContractClasses">合約類別</a>一節) 中。 892 893 894 您也可以呼叫 {@link android.database.Cursor#getType 895Cursor.getType()} 來確認可用的資料類型。 896</p> 897<p> 898 此外,供應程式也會保留任何所定義內容 URI 的 MIME 資料類型資訊。您可以利用 MIME 類型資訊確認您的應用程式是否可處理供應程式所提供的資料,或是根據 MIME 類型選擇處理方式類型。 899 900當您使用內含複雜的資料結構或檔案的供應程式時,通常需要使用 MIME 類型。 901 902例如,聯絡人供應程式中的 {@link android.provider.ContactsContract.Data} 表格會使用 MIME 類型為每個資料列中儲存的聯絡人資料加上標籤。 903 904如要取得與內容 URI 相對應的 MIME 類型,請呼叫 905{@link android.content.ContentResolver#getType ContentResolver.getType()}。 906</p> 907<p> 908 如要瞭解標準與自訂 MIME 類型的語法,請參閱 <a href="#MIMETypeReference">MIME 類型參考資料</a>。 909 910</p> 911 912 913<!-- Alternative Forms of Provider Access --> 914<h2 id="AltForms">供應程式存取權的替代形式</h2> 915<p> 916 開發應用程式時會使用到 3 種供應程式存取權的替代形式: 917</p> 918<ul> 919 <li> 920 <a href="#Batch">批次存取</a>:您可以使用 {@link android.content.ContentProviderOperation} 類別中的方法建立批次存取權呼叫,然後利用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 套用這些呼叫。 921 922 923 </li> 924 <li> 925 非同步查詢:建議您在個別執行緒中進行查詢。您可以使用 {@link android.content.CursorLoader} 類別來完成這項作業。 926如需相關示範說明,請參閱<a href="{@docRoot}guide/components/loaders.html">載入器</a>指南中的範例。 927 928 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 如要以「批次模式」存取供應程式,請建立一系列 {@link android.content.ContentProviderOperation} 物件,然後使用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 將這些物件分派給內容供應程式。 947 948 949請將內容供應程式的「授權」<em></em>(而不是特定內容 URI) 傳入這個方法。這樣可讓陣列中的所有 {@link android.content.ContentProviderOperation} 物件能夠在不同表格中運作。 950 951 952如果您呼叫 {@link android.content.ContentResolver#applyBatch 953ContentResolver.applyBatch()},則系統會傳回一系列結果。 954</p> 955<p> 956 {@link android.provider.ContactsContract.RawContacts} 合約類別的說明包含可展示批次插入作業的程式碼片段。 957<a href="{@docRoot}resources/samples/ContactManager/index.html">聯絡人管理員</a>範例應用程式的 <code>ContactAdder.java</code> 來源檔案包含批次存取範例供您參考。 958 959 960 961</p> 962<div class="sidebox-wrapper"> 963<div class="sidebox"> 964<h2>使用協助程式顯示資料</h2> 965<p> 966 如果您的應用程式「沒有」<em></em>存取權限,而您仍想使用意圖在其他應用程式中顯示資料。 967例如,日曆應用程式接受用於顯示特定日期或活動的 {@link android.content.Intent#ACTION_VIEW} 意圖。 968 969 這樣可讓您顯示日曆資訊,而不必自行建立使用者介面。如要進一步瞭解此功能,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html">日曆供應程式</a>指南。 970 971 972</p> 973<p> 974 您傳送意圖的目標應用程式未必要與供應程式建立關聯。 975例如,您可以從聯絡人供應程式擷取聯絡人資料,然後將內含聯絡人圖片內容 URI 的 {@link android.content.Intent#ACTION_VIEW} 意圖傳送到圖片檢視器。 976 977 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 即使沒有適當的存取權限,您仍可存取內容供應程式的資料,方法是傳送意圖到沒有權限的應用程式,然後接收內含「URI」權限的結果意圖。 991 992 993 URI 權限是特定內容 URI 專用的權限;在接收權限的 Activity 結束之前,這類權限會維持有效狀態。 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>這些旗標並不會將讀取或寫入存取權授予系統在內容 URI 中提供授權的供應程式。存取權僅供 URI 使用。 1009 1010</p> 1011<p> 1012 供應程式會使用 1013<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 1014 元素的 1015<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code> 1016 屬性以及 1017<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 1018 元素的 1019<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></code> 1020 子元素在本身的宣示說明中為內容 URI 定義 URI 權限。如要進一步瞭解 URI 權限的運作機制,請參閱<a href="{@docRoot}guide/topics/security/security.html">安全性和權限</a>指南的「URI 權限」。 1021 1022 1023</p> 1024<p> 1025 例如,即使您沒有 {@link android.Manifest.permission#READ_CONTACTS} 權限,您仍可透過聯絡人供應程式擷取聯絡人資料。 1026您可能會想透過可用來在聯絡人的生日當天傳送電子賀卡給對方的應用程式進行這項動作。 1027您偏好讓使用者控管要讓您的應用程式使用的聯絡人資料,而不是要求 {@link android.Manifest.permission#READ_CONTACTS} 授權您存取使用者的所有聯絡人以及個人資訊。 1028 1029 1030為了達到這個目的,您進行了下列程序: 1031</p> 1032<ol> 1033 <li> 1034 您的應用程式使用 {@link android.app.Activity#startActivityForResult 1035startActivityForResult()} 方法,傳送了內含 1036{@link android.content.Intent#ACTION_PICK} 動作的意圖以及「聯絡人」MIME 類型 1037{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}。 1038 1039 </li> 1040 <li> 1041 由於該意圖符合聯絡人應用程式的「選取」Activity 的意圖篩選器,因此該 Activity 會在移動到前景。 1042 1043 </li> 1044 <li> 1045 在選取 Activity 中,使用者選取了要更新的聯絡人。 1046一旦使用者進行這項動作,選取 Activity 便會呼叫 1047{@link android.app.Activity#setResult setResult(resultcode, intent)} 來設定要傳回您應用程式的意圖。 1048該意圖包含使用者所選聯絡人的內容 URI,以及「額外」的旗標 1049{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}。 1050這些旗標可將 URI 權限授予您的應用程式,以便其讀取內容 URI 指向的聯絡人資料。選取 Activity 隨後會呼叫 {@link android.app.Activity#finish()} 來傳回您應用程式的控制權。 1051 1052 1053 1054 </li> 1055 <li> 1056 您的 Activity 返回前景,而系統呼叫您 Activity 的 1057{@link android.app.Activity#onActivityResult onActivityResult()} 方法。 1058這個方法可接收聯絡人應用程式中的選取 Activity 所建立的結果意圖。 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 例如,日曆應用程式接受可讓您啟用應用程式插入 UI 的 1075{@link android.content.Intent#ACTION_INSERT} 意圖。您可以在該意圖中傳入「額外」的資料,供應用程式用於預先填入使用者介面。由於週期性活動的語法較為複雜,因此建議您利用 {@link android.content.Intent#ACTION_INSERT} 啟用日曆應用程式,然後讓使用者透過該應用程式將活動插入日曆供應程式。 1076 1077 1078 1079 1080</p> 1081<!-- Contract Classes --> 1082<h2 id="ContractClasses">合約類別</h2> 1083<p> 1084 合約類別可定義協助應用程式使用內容 URI、欄名稱、意圖動作,以及內容供應程式的其他功能的常數。 1085合約類別並不會自動納入供應程式;供應程式的開發人員必須自行定義合約類別,然後將其提供給其他開發人員。 1086 1087Android 平台內建的大多數供應程式都可在 {@link android.provider} 套件中取得對應的合約類別。 1088 1089</p> 1090<p> 1091 例如,使用者字典供應程式有一個內含內容 URI 和欄名稱常數的 {@link android.provider.UserDictionary} 合約類別。 1092「字詞」表格的內容 URI 是在 1093{@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI} 常數中定義。 1094 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 媒體類型或自訂媒體類型字串,或是以上兩者。 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> 類型以及 <code>html</code> 子類型。 1128如果供應程式傳回這種 URI 類型,代表採用該 URI 的查詢會傳回內含 HTML 標記的文字。 1129 1130</p> 1131<p> 1132 自訂 MIME 類型字串 (亦稱為「廠商專用」MIME 類型) 包含較為複雜的類型<em></em>和子類型<em></em>值。 1133針對多個資料欄,類型值<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例如,當聯絡人應用程式建立電話號碼資料列時,會為該列設定下列 MIME 類型: 1150 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> 且包括 Line1、Line2 和 Line3 表格。 1162根據 Line1 表格的內容 URI 1163</p> 1164<p> 1165<pre> 1166content://com.example.trains/Line1 1167</pre> 1168<p> 1169 供應程式會傳回 MIME 類型 1170</p> 1171<pre> 1172vnd.android.cursor.<strong>dir</strong>/vnd.example.line1 1173</pre> 1174<p> 1175 根據 Line1 表格的內容 URI 1176</p> 1177<pre> 1178content://com.example.trains/Line2/5 1179</pre> 1180<p> 1181 供應程式會傳回 MIME 類型 1182</p> 1183<pre> 1184vnd.android.cursor.<strong>item</strong>/vnd.example.line2 1185</pre> 1186<p> 1187 大多數內容供應程式會針對其使用的 MIME 類型定義合約類別常數。例如,聯絡人供應程式的合約類別 {@link android.provider.ContactsContract.RawContacts} 會為某個 MIME 類型的原始聯絡人列定義 {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} 常數。 1188 1189 1190 1191 1192</p> 1193<p> 1194 如要進一步瞭解個別資料列的內容 URI,請參閱<a href="#ContentURIs">內容 URI</a>。 1195 1196</p> 1197