1page.title=Nội dung Cơ bản về Trình cung cấp Nội dung 2@jd:body 3<div id="qv-wrapper"> 4<div id="qv"> 5<!-- In this document --> 6<h2>Trong tài liệu này</h2> 7<ol> 8 <li> 9 <a href="#Basics">Tổng quan</a> 10 <ol> 11 <li> 12 <a href="#ClientProvider">Truy cập một trình cung cấp</a> 13 </li> 14 <li> 15 <a href="#ContentURIs">URI Nội dung</a> 16 </li> 17 </ol> 18 </li> 19 <li> 20 <a href="#SimpleQuery">Truy xuất Dữ liệu từ Trình cung cấp</a> 21 <ol> 22 <li> 23 <a href="#RequestPermissions">Yêu cầu quyền truy cập đọc</a> 24 </li> 25 <li> 26 <a href="#Query">Xây dựng truy vấn</a> 27 </li> 28 <li> 29 <a href="#DisplayResults">Hiển thị các kết quả truy vấn</a> 30 </li> 31 <li> 32 <a href="#GettingResults">Lấy dữ liệu từ các kết quả truy vấn</a> 33 </li> 34 </ol> 35 </li> 36 <li> 37 <a href="#Permissions">Quyền của Trình cung cấp Nội dung</a> 38 </li> 39 <li> 40 <a href="#Modifications">Chèn, Cập nhật, và Xóa Dữ liệu</a> 41 <ol> 42 <li> 43 <a href="#Inserting">Chèn dữ liệu</a> 44 </li> 45 <li> 46 <a href="#Updating">Cập nhật dữ liệu</a> 47 </li> 48 <li> 49 <a href="#Deleting">Xóa dữ liệu</a> 50 </li> 51 </ol> 52 </li> 53 <li> 54 <a href="#DataTypes">Các Kiểu Dữ liệu của Trình cung cấp</a> 55 </li> 56 <li> 57 <a href="#AltForms">Các Hình thức Truy cập Trình cung cấp Thay thế</a> 58 <ol> 59 <li> 60 <a href="#Batch">Truy cập hàng loạt</a> 61 </li> 62 <li> 63 <a href="#Intents">Truy cập dữ liệu thông qua ý định</a> 64 </li> 65 </ol> 66 </li> 67 <li> 68 <a href="#ContractClasses">Các Lớp Hợp đồng</a> 69 </li> 70 <li> 71 <a href="#MIMETypeReference">Tham khảo Kiểu MIME</a> 72 </li> 73</ol> 74 75 <!-- Key Classes --> 76<h2>Lớp khóa</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>Các Mẫu Liên quan</h2> 94 <ol> 95 <li> 96 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List2.html"> 97 Con chạy (Danh bạ)</a> 98 </li> 99 <li> 100 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html"> 101 Con chạy (Điện thoại)</a> 102 </li> 103 </ol> 104 105 <!-- See also --> 106<h2>Xem thêm</h2> 107 <ol> 108 <li> 109 <a href="{@docRoot}guide/topics/providers/content-provider-creating.html"> 110 Tạo một Trình cung cấp Nội dung</a> 111 </li> 112 <li> 113 <a href="{@docRoot}guide/topics/providers/calendar-provider.html"> 114 Trình cung cấp Lịch</a> 115 </li> 116 </ol> 117</div> 118</div> 119 120 <!-- Intro paragraphs --> 121<p> 122 Trình cung cấp nội dung quản lý truy cập vào một kho dữ liệu tập trung. Trình cung cấp 123 là bộ phận của một ứng dụng Android, nó thường cung cấp UI của chính mình để làm việc cùng 124 dữ liệu. Tuy nhiên, trình cung cấp nội dung được thiết kế chủ yếu cho các ứng dụng khác 125 sử dụng, giúp truy cập trình cung cấp bằng cách sử dụng một đối tượng máy khách cung cấp. Cùng nhau, trình cung cấp 126 và máy khách cung cấp sẽ mang đến một giao diện nhất quán, tiêu chuẩn cho dữ liệu, giao diện này 127 cũng đồng thời xử lý truyền thông liên tiến trình và bảo mật truy cập dữ liệu. 128</p> 129<p> 130 Chủ đề này đề cập đến những nội dung cơ bản sau đây: 131</p> 132 <ul> 133 <li>Cách trình cung cấp nội dung hoạt động.</li> 134 <li>API bạn sử dụng để truy xuất dữ liệu từ một trình cung cấp nội dung.</li> 135 <li>API bạn sử dụng để chèn, cập nhật, hoặc xóa dữ liệu trong một trình cung cấp nội dung.</li> 136 <li>Các tính năng API khác tạo điều kiện làm việc cùng các trình cung cấp.</li> 137 </ul> 138 139 <!-- Basics --> 140<h2 id="Basics">Tổng quan</h2> 141<p> 142 Trình cung cấp nội dung trình bày dữ liệu cho các ứng dụng bên ngoài dưới dạng một hoặc nhiều bảng tương tự 143 như các bảng được tìm thấy trong một cơ sở dữ liệu quan hệ. Mỗi hàng thể hiện một thực thể của một số kiểu dữ liệu 144 mà trình cung cấp thu thập, và mỗi cột trong hàng thể hiện một phần riêng biệt của dữ liệu được thu thập 145 đối với một thực thể. 146</p> 147<p> 148 Ví dụ, một trong các trình cung cấp tích hợp trong nền tảng Android đó là từ điển người dùng, nó 149 lưu giữ chính tả của những từ phi tiêu chuẩn mà người dùng muốn giữ lại. Bảng 1 minh họa 150 cách mà dữ liệu có thể được trình bày trong bảng của trình cung cấp này: 151</p> 152<p class="table-caption"> 153 <strong>Bảng 1:</strong> Bảng từ điển người dùng mẫu. 154</p> 155<table id="table1" style="width: 50%;"> 156 <tr> 157 <th style="width:20%" align="center" scope="col">từ</th> 158 <th style="width:20%" align="center" scope="col">id ứng dụng</th> 159 <th style="width:20%" align="center" scope="col">tần suất</th> 160 <th style="width:20%" align="center" scope="col">bản địa</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 Trong bảng 1, mỗi hàng thể hiện một thực thể của một từ mà có thể không thấy có 201 trong từ điển chuẩn. Mỗi cột thể hiện một số dữ liệu cho từ đó, chẳng hạn như 202 từ bản địa được dùng lần đầu cho từ đó. Tiêu đề cột là các tên cột được lưu giữ trong 203 trình cung cấp. Để tham khảo tới bản địa của một hàng, bạn tham khảo tới cột <code>locale</code> của hàng đó. Đối với 204 trình cung cấp này, cột <code>_ID</code> đóng vai trò là cột "khóa chính" mà 205 trình cung cấp tự động duy trì. 206</p> 207<p class="note"> 208 <strong>Lưu ý:</strong> Trình cung cấp không bắt buộc phải có một khóa chính, và không bắt buộc phải 209 sử dụng <code>_ID</code> làm tên cột của một khóa chính nếu có khóa. Tuy nhiên, 210 nếu bạn muốn gắn kết dữ liệu từ một trình cung cấp với một {@link android.widget.ListView}, một trong các 211 tên cột sẽ phải là <code>_ID</code>. Yêu cầu này được giải thích chi tiết hơn trong 212 phần <a href="#DisplayResults">Hiển thị các kết quả truy vấn</a>. 213</p> 214<h3 id="ClientProvider">Truy cập một trình cung cấp</h3> 215<p> 216 Một ứng dụng truy cập dữ liệu từ một trình cung cấp nội dung bằng 217 một đối tượng máy khách {@link android.content.ContentResolver}. Đối tượng này có các phương pháp để gọi 218 những phương pháp có tên giống nhau trong đối tượng trình cung cấp, một thực thể của một trong những lớp con 219 cụ thể của {@link android.content.ContentProvider}. Các phương pháp 220 {@link android.content.ContentResolver} cung cấp các chức năng 221 "CRUD" (tạo, truy xuất, cập nhật, và xóa) cơ bản của thiết bị lưu trữ liên tục. 222</p> 223<p> 224 Đối tượng {@link android.content.ContentResolver} trong tiến trình 225 của ứng dụng máy khách và đối tượng {@link android.content.ContentProvider} trong ứng dụng mà sở hữu 226 trình cung cấp sẽ tự động xử lý truyền thông liên tiến trình. 227 {@link android.content.ContentProvider} cũng đóng vai trò như một lớp rút gọn giữa kho dữ liệu 228 của nó và biểu diễn bên ngoài của dữ liệu dưới dạng bảng. 229</p> 230<p class="note"> 231 <strong>Lưu ý:</strong> Để truy cập một trình cung cấp, ứng dụng của bạn thường phải yêu cầu các quyền 232 cụ thể trong tệp bản kê khai của mình. Điều này được mô tả chi tiết hơn trong phần 233 <a href="#Permissions">Quyền của Trình cung cấp Nội dung</a> 234</p> 235<p> 236 Ví dụ, để có một danh sách các từ và nội dung bản địa của chúng từ Trình cung cấp Từ điển Người dùng, 237 bạn hãy gọi {@link android.content.ContentResolver#query ContentResolver.query()}. 238 Phương pháp {@link android.content.ContentResolver#query query()} sẽ gọi phương pháp 239 {@link android.content.ContentProvider#query ContentProvider.query()} được định nghĩa bởi 240 Trình cung cấp Từ điển Người dùng. Các dòng mã sau thể hiện một lệnh gọi 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 Bảng 2 cho biết các tham đối tới 254 {@link android.content.ContentResolver#query 255 query(Uri,projection,selection,selectionArgs,sortOrder)} khớp với một câu lệnh SQL SELECT như thế nào: 256</p> 257<p class="table-caption"> 258 <strong>Bảng 2:</strong> Query() so với truy vấn SQL. 259</p> 260<table id="table2" style="width: 75%;"> 261 <tr> 262 <th style="width:25%" align="center" scope="col">tham đối query()</th> 263 <th style="width:25%" align="center" scope="col">Từ khóa/tham số SELECT</th> 264 <th style="width:50%" align="center" scope="col">Lưu ý</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> ánh xạ tới bảng trong trình cung cấp có tên <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> là một mảng gồm các cột nên được đưa vào đối với mỗi hàng 276 được truy xuất. 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> quy định các tiêu chí để lựa chọn hàng.</td> 283 </tr> 284 <tr> 285 <td align="center"><code>selectionArgs</code></td> 286 <td align="center"> 287 (Không có sự tương đương chính xác. Các tham đối lựa chọn sẽ thay thế các chỗ dành sẵn <code>?</code> trong 288 mệnh đề lựa chọn.) 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> quy định thứ tự các hàng xuất hiện trong 296 {@link android.database.Cursor} được trả về. 297 </td> 298 </tr> 299</table> 300<h3 id="ContentURIs">URI Nội dung</h3> 301<p> 302 <strong>URI nội dung</strong> là một URI xác định dữ liệu trong một trình cung cấp. URI nội dung 303 bao gồm tên biểu tượng của toàn bộ trình cung cấp (<strong>quyền</strong> của nó) và một 304 tên trỏ đến một bảng (<strong>đường dẫn</strong>). Khi bạn gọi 305 một phương pháp máy khách để truy cập một bảng trong một trình cung cấp, URI nội dung cho bảng là một trong các 306 tham đối. 307</p> 308<p> 309 Trong các dòng mã trước, hằng số 310 {@link android.provider.UserDictionary.Words#CONTENT_URI} chứa URI nội dung của 311 bảng "từ" của từ điển người dùng. Đối tượng {@link android.content.ContentResolver} 312 sẽ phân tích quyền của URI, và sử dụng nó để "giải quyết" trình cung cấp bằng cách 313 so sánh quyền với một bảng hệ thống của các trình cung cấp đã biết. Khi đó, 314 {@link android.content.ContentResolver} có thể phân phối các tham đối truy vấn tới đúng 315 trình cung cấp. 316</p> 317<p> 318 {@link android.content.ContentProvider} sử dụng phần đường dẫn của URI nội dung nhằm chọn 319 bảng để truy cập. Trình cung cấp thường có một <strong>đường dẫn</strong> cho mỗi bảng mà nó hiện ra. 320</p> 321<p> 322 Trong các dòng mã trước, URI đầy đủ cho bảng "từ" là: 323</p> 324<pre> 325content://user_dictionary/words 326</pre> 327<p> 328 trong đó xâu <code>user_dictionary</code> là quyền của trình cung cấp, và 329xâu <code>words</code> là đường dẫn của bảng. Xâu 330 <code>content://</code> (<strong>lược đồ</strong>) sẽ luôn có mặt, 331 và xác định đây là một URI nội dung. 332</p> 333<p> 334 Nhiều trình cung cấp cho phép bạn truy cập một hàng đơn lẻ trong một bảng bằng cách nối một giá trị ID 335 với đuôi của URI. Ví dụ, để truy xuất một hàng có <code>_ID</code> là 336 <code>4</code> từ một từ điển người dùng, bạn có thể sử dụng URI nội dung này: 337</p> 338<pre> 339Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4); 340</pre> 341<p> 342 Bạn thường sử dụng các giá trị id khi bạn đã truy xuất một tập hợp các hàng, sau đó muốn cập nhật hoặc xóa 343 một trong số chúng. 344</p> 345<p class="note"> 346 <strong>Lưu ý:</strong> Các lớp {@link android.net.Uri} và {@link android.net.Uri.Builder} 347 chứa các phương pháp thuận tiện để xây dựng đối tượng URI định dạng tốt từ các xâu. 348 {@link android.content.ContentUris} chứa các phương pháp thuận tiện để nối các giá trị id với 349 một URI. Đoạn mã HTML trước sử dụng {@link android.content.ContentUris#withAppendedId 350withAppendedId()} để nối một id với URI nội dung Từ điển Người dùng. 351</p> 352 353 354 <!-- Retrieving Data from the Provider --> 355<h2 id="SimpleQuery">Truy xuất Dữ liệu từ Trình cung cấp</h2> 356<p> 357 Phần này mô tả cách truy xuất dữ liệu từ một trình cung cấp bằng cách sử dụng Trình cung cấp Từ điển Người dùng 358 làm ví dụ. 359</p> 360<p class="note"> 361 Để giải thích rõ, đoạn mã HTML trong phần này gọi 362 {@link android.content.ContentResolver#query ContentResolver.query()} trên "luồng UI"". Tuy nhiên, trong 363 mã thực sự, bạn nên thực hiện các truy vấn không đồng bộ trên một luồng riêng. Một cách để làm 364 điều này đó là sử dụng lớp {@link android.content.CursorLoader}, nó được mô tả chi tiết hơn 365 trong hướng dẫn <a href="{@docRoot}guide/components/loaders.html"> 366 Trình tải</a>. Bênh cạnh đó, các dòng mã chỉ là đoạn mã HTML; chúng không thể hiện một ứng dụng 367 hoàn chỉnh. 368</p> 369<p> 370 Để truy xuất dữ liệu từ một trình cung cấp, hãy làm theo các bước cơ bản sau: 371</p> 372<ol> 373 <li> 374 Yêu cầu quyền truy cập đọc cho trình cung cấp. 375 </li> 376 <li> 377 Định nghĩa mã để gửi một truy vấn tới trình cung cấp. 378 </li> 379</ol> 380<h3 id="RequestPermissions">Yêu cầu quyền truy cập đọc</h3> 381<p> 382 Để truy xuất dữ liệu từ một trình cung cấp, ứng dụng của bạn cần "quyền truy cập đọc" cho 383 trình cung cấp. Bạn không thể yêu cầu quyền này trong thời gian chạy; thay vào đó, bạn phải chỉ định rằng 384 bạn cần quyền này trong bản kê khai của mình bằng cách sử dụng phần tử 385<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 386 và tên quyền chính xác được định nghĩa bởi 387 trình cung cấp. Khi bạn chỉ định phần tử này trong bản kê khai của mình, bạn đang thực tế hóa "yêu cầu" quyền 388 này cho ứng dụng của mình. Khi người dùng cài đặt ứng dụng của bạn, họ ngầm hiểu cấp 389 yêu cầu này. 390</p> 391<p> 392 Để tìm tên chính xác của quyền truy cập đọc cho trình cung cấp bạn đang sử dụng, cũng như 393 tên cho các quyền truy cập khác được sử dụng bởi trình truy cập, hãy xem trong tài liệu 394 của trình cung cấp. 395</p> 396<p> 397 Vai trò của quyền trong việc truy cập các trình cung cấp được mô tả chi tiết hơn trong phần 398 <a href="#Permissions">Quyền của Trình cung cấp Nội dung</a>. 399</p> 400<p> 401 Trình cung cấp Từ điển Người dùng sẽ định nghĩa quyền 402 <code>android.permission.READ_USER_DICTIONARY</code> trong tệp bản kê khai của nó, vì vậy một 403 ứng dụng muốn đọc từ trình cung cấp sẽ phải yêu cầu quyền này. 404</p> 405<!-- Constructing the query --> 406<h3 id="Query">Xây dựng truy vấn</h3> 407<p> 408 Bước tiếp theo trong việc truy xuất dữ liệu từ một trình cung cấp đó là xây dựng một truy vấn. Đoạn mã HTML đầu tiên 409 này định nghĩa một số biến cho việc truy cập Trình cung cấp Từ điển Người dùng: 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 Đoạn mã HTML tiếp theo cho biết cách sử dụng 430 {@link android.content.ContentResolver#query ContentResolver.query()}, bằng cách sử dụng Trình cung cấp Từ điển 431 Người dùng như một ví dụ. Truy vấn máy khách trình cung cấp tương tự như một truy vấn SQL, và nó chứa một 432 tập hợp các cột để trả về, một tập hợp các tiêu chí lựa chọn, và một thứ tự sắp xếp. 433</p> 434<p> 435 Tập hợp các cột mà truy vấn cần trả về được gọi là <strong>dự thảo</strong> 436 (biến <code>mProjection</code>). 437</p> 438<p> 439 Biểu thức để chỉ định các hàng cần truy xuất sẽ được chia thành một mệnh đề lựa chọn và 440 tham đối lựa chọn. Mệnh đề lựa chọn là sự kết hợp giữa các biểu thức lô-gic và biểu thức Boolean, 441 tên cột, và giá trị (biến <code>mSelectionClause</code>). Nếu bạn chỉ định 442 tham số thay thế được <code>?</code> thay vì một giá trị, phương pháp truy vấn sẽ truy xuất giá trị 443 từ mảng tham đối lựa chọn (biến <code>mSelectionArgs</code>). 444</p> 445<p> 446 Trong đoạn mã HTML tiếp theo, nếu người dùng không điền từ thì mệnh đề lựa chọn được đặt thành 447 <code>null</code>, và truy vấn trả về tất cả các từ trong trình cung cấp. Nếu người dùng nhập 448 một từ, mệnh đề lựa chọn được đặt thành <code>UserDictionary.Words.WORD + " = ?"</code> và 449 phần tử đầu tiên của mảng tham đối lựa chọn được đặt thành từ mà người dùng đã nhập. 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 Truy vấn này tương tự như câu lệnh SQL: 508</p> 509<pre> 510SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC; 511</pre> 512<p> 513 Trong câu lệnh SQL này, tên cột thực tế được sử dụng thay vì các hằng số lớp hợp đồng. 514</p> 515<h4 id="Injection">Bảo vệ trước mục nhập độc hại</h4> 516<p> 517 Nếu dữ liệu được quản lý bởi trình cung cấp nội dung nằm trong một cơ sở dữ liệu SQL, việc điền dữ liệu không được tin cậy từ bên ngoài 518 vào các câu lệnh SQL thô có thể dẫn đến tiêm lỗi SQL. 519</p> 520<p> 521 Hãy xét mệnh đề lựa chọn sau: 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 Nếu bạn làm vậy, bạn đang cho phép người dùng ghép nối SQL độc hại lên câu lệnh SQL của mình. 529 Ví dụ, người dùng có thể điền "nothing; DROP TABLE *;" cho <code>mUserInput</code>, làm vậy 530 sẽ dẫn đến mệnh đề lựa chọn <code>var = nothing; DROP TABLE *;</code>. Do 531 mệnh đề lựa chọn được coi như một câu lệnh SQL, điều này có thể khiến trình cung cấp xóa tất cả 532 bảng trong cơ sở dữ liệu SQLite cơ bản (trừ khi trình cung cấp được thiết lập để bắt những lần thử 533 <a href="http://en.wikipedia.org/wiki/SQL_injection">tiêm lỗi SQL</a>). 534</p> 535<p> 536 Để tránh vấn đề này, hãy sử dụng một mệnh đề lựa chọn mà sử dụng <code>?</code> làm tham số 537 thay thế được và một mảng các tham đối lựa chọn riêng. Khi bạn làm như vậy, mục nhập của người dùng 538 được gắn kết trực tiếp với truy vấn thay vì được giải nghĩa như một phần của câu lệnh SQL. 539 Vì nó không được coi như SQL, mục nhập của người dùng không thể tiêm lỗi SQL độc hại. Thay vì sử dụng 540 ghép nối để điền mục nhập của người dùng, hãy sử dụng mệnh đề lựa chọn này: 541</p> 542<pre> 543// Constructs a selection clause with a replaceable parameter 544String mSelectionClause = "var = ?"; 545</pre> 546<p> 547 Thiết lập mảng các tham đối lựa chọn như sau: 548</p> 549<pre> 550// Defines an array to contain the selection arguments 551String[] selectionArgs = {""}; 552</pre> 553<p> 554 Đặt một giá trị trong mảng các tham đối lựa chọn như sau: 555</p> 556<pre> 557// Sets the selection argument to the user's input 558selectionArgs[0] = mUserInput; 559</pre> 560<p> 561 Mệnh đề lựa chọn mà sử dụng <code>?</code> như một tham số thay thế được và một mảng 562 các tham đối lựa chọn là cách được ưu tiên để chỉ định một lựa chọn, ngay cả khi trình cung cấp không 563 được dựa trên cơ sở dữ liệu SQL. 564</p> 565<!-- Displaying the results --> 566<h3 id="DisplayResults">Hiển thị các kết quả truy vấn</h3> 567<p> 568 Phương pháp máy khách {@link android.content.ContentResolver#query ContentResolver.query()} luôn trả về 569 một {@link android.database.Cursor} chứa các cột được chỉ định bởi dự thảo của 570 truy vấn cho các hàng khớp với các tiêu chí lựa chọn của truy vấn. Một đối tượng 571 {@link android.database.Cursor} cung cấp truy cập đọc ngẫu nhiên vào các hàng và cột mà nó 572 chứa. Bằng cách sử dụng phương pháp {@link android.database.Cursor}, bạn có thể lặp lại các hàng trong 573 kết quả, xác định kiểu dữ liệu của từng cột, lấy dữ liệu ra khỏi cột, và kiểm tra các tính chất khác 574 của kết quả. Một số triển khai {@link android.database.Cursor} sẽ tự động 575 cập nhật đối tượng khi dữ liệu của trình cung cấp thay đổi, hoặc kích khởi các phương pháp trong một đối tượng quan sát 576 khi {@link android.database.Cursor} thay đổi, hoặc cả hai. 577</p> 578<p class="note"> 579 <strong>Lưu ý:</strong> Một trình cung cấp có thể hạn chế truy cập vào các cột dựa trên tính chất của 580 đối tượng thực hiện truy vấn. Ví dụ, Trình cung cấp Danh bạ hạn chế truy cập đối với một số cột cho 581 các trình điều hợp đồng bộ, vì thế nó sẽ không trả chúng về một hoạt động hay dịch vụ. 582</p> 583<p> 584 Nếu không hàng nào khớp với các tiêu chí lựa chọn, trình cung cấp 585 sẽ trả về một đối tượng {@link android.database.Cursor} mà trong đó 586 {@link android.database.Cursor#getCount Cursor.getCount()} bằng 0 (con chạy trống). 587</p> 588<p> 589 Nếu xảy ra một lỗi nội bộ, các kết quả của truy vấn sẽ phụ thuộc vào trình cung cấp cụ thể. Nó có thể 590 chọn trả về <code>null</code>, hoặc nó có thể đưa ra một lỗi {@link java.lang.Exception}. 591</p> 592<p> 593 Do {@link android.database.Cursor} là một "danh sách" hàng, một cách hay để hiển thị 594 nội dung của một {@link android.database.Cursor} đó là liên kết nó với một {@link android.widget.ListView} 595 thông qua một {@link android.widget.SimpleCursorAdapter}. 596</p> 597<p> 598 Đoạn mã HTML sau tiếp tục từ đoạn mã HTML trước. Nó tạo một đối tượng 599 {@link android.widget.SimpleCursorAdapter} chứa {@link android.database.Cursor} 600 được truy xuất bởi truy vấn, và đặt đối tượng này thành trình điều hợp cho một 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>Lưu ý:</strong> Để lùi {@link android.widget.ListView} bằng một 628 {@link android.database.Cursor}, con chạy phải chứa một cột có tên <code>_ID</code>. 629 Vì điều này, truy vấn được hiện lúc trước truy xuất cột <code>_ID</code> cho 630 bảng "từ" mặc dù {@link android.widget.ListView} không hiển thị nó. 631 Hạn chế này cũng giải thích lý do tại sao phần lớn trình cung cấp đều có một cột <code>_ID</code> cho mỗi 632 bảng của nó. 633</p> 634 635 <!-- Getting data from query results --> 636<h3 id="GettingResults">Lấy dữ liệu từ các kết quả truy vấn</h3> 637<p> 638 Thay vì chỉ hiển thị các kết quả truy vấn, bạn có thể sử dụng chúng cho các tác vụ khác. Ví 639 dụ, bạn có thể truy xuất chính tả từ một từ điển người dùng, rồi sau đó tìm kiếm từ đó trong 640 các trình cung cấp khác. Để làm điều này, bạn lặp lại các hàng trong {@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 Triển khai {@link android.database.Cursor} sẽ chứa một vài phương pháp “get" để 676 truy xuất các kiểu dữ liệu khác nhau từ đối tượng. Ví dụ, đoạn mã HTML trước 677 sử dụng {@link android.database.Cursor#getString getString()}. Chúng cũng có một phương pháp 678 {@link android.database.Cursor#getType getType()} để trả về một giá trị cho biết 679 kiểu dữ liệu của cột. 680</p> 681 682 683 <!-- Requesting permissions --> 684<h2 id="Permissions">Quyền của Trình cung cấp Nội dung</h2> 685<p> 686 Ứng dụng của một trình cung cấp có thể chỉ định các quyền mà ứng dụng khác có thể có để 687 truy cập dữ liệu của trình cung cấp đó. Những quyền này đảm bảo rằng người dùng biết một ứng dụng 688 sẽ cố gắng truy cập dữ liệu nào. Dựa trên các yêu cầu của trình cung cấp, các ứng dụng khác 689 yêu cầu quyền mà chúng cần để truy cập trình dữ liệu. Người dùng cuối thấy các quyền 690 được yêu cầu khi họ cài đặt ứng dụng. 691</p> 692<p> 693 Nếu ứng dụng của một trình cung cấp không chỉ định bất kỳ quyền nào, khi đó các ứng dụng khác không có 694 quyền truy cập dữ liệu của trình cung cấp. Tuy nhiên, các thành phần trong ứng dụng của trình cung cấp luôn có 695 đầy đủ quyền truy nhập đọc và ghi, không phụ thuộc vào các quyền được chỉ định. 696</p> 697<p> 698 Như đã lưu ý, Trình cung cấp Từ điển Người dùng sẽ yêu cầu 699 quyền <code>android.permission.READ_USER_DICTIONARY</code> để truy xuất dữ liệu từ nó. 700 Trình cung cấp có quyền <code>android.permission.WRITE_USER_DICTIONARY</code> 701 riêng để chèn, cập nhật, hoặc xóa dữ liệu. 702</p> 703<p> 704 Để nhận các quyền cần để truy cập một trình cung cấp, ứng dụng yêu cầu chúng bằng một phần tử 705<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 706 trong tệp bản kê khai của nó. Khi Trình quản lý Gói Android cài đặt các ứng dụng, người dùng 707 phải phê chuẩn tất cả quyền mà ứng dụng yêu cầu. Nếu người dùng phê chuẩn tất cả quyền, khi đó 708 Trình quản lý Gói sẽ tiếp tục cài đặt; nếu người dùng không phê chuẩn chúng, Trình quản lý Gói sẽ 709 hủy bỏ việc cài đặt. 710</p> 711<p> 712 Phần tử 713<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 714 sau yêu cầu quyền truy cập đọc vào Trình cung cấp Từ điển Người dùng: 715</p> 716<pre> 717 <uses-permission android:name="android.permission.READ_USER_DICTIONARY"> 718</pre> 719<p> 720 Tác động của các quyền tới việc truy cập trình cung cấp được giải thích chi tiết hơn trong 721 hướng dẫn <a href="{@docRoot}guide/topics/security/security.html">Bảo mật và Quyền</a>. 722</p> 723 724 725<!-- Inserting, Updating, and Deleting Data --> 726<h2 id="Modifications">Chèn, Cập nhật, và Xóa Dữ liệu</h2> 727<p> 728 Giống như cách bạn truy xuất dữ liệu từ một trình cung cấp, bạn cũng có thể sử dụng tương tác giữa 729 một máy khách cung cấp và {@link android.content.ContentProvider} của trình cung cấp để sửa đổi dữ liệu. 730 Bạn gọi một phương pháp {@link android.content.ContentResolver} với các tham đối được chuyển sang 731 phương pháp {@link android.content.ContentProvider} tương ứng. Trình cung cấp và 732 máy khách cung cấp sẽ tự động xử lý bảo mật và truyền thông liên tiến trình. 733</p> 734<h3 id="Inserting">Chèn dữ liệu</h3> 735<p> 736 Để chèn dữ liệu vào một trình cung cấp, bạn gọi phương pháp 737 {@link android.content.ContentResolver#insert ContentResolver.insert()} 738. Phương pháp này chèn một hàng mới vào trình cung cấp và trả về một URI nội dung cho hàng đó. 739 Đoạn mã HTML này cho biết cách chèn một từ mới vào Trình cung cấp Từ điển Người dùng: 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 Dữ liệu cho hàng mới đi vào trong một đối tượng {@link android.content.ContentValues} đơn lẻ, đối tượng này 766 có dạng tương tự như con chạy một hàng. Các cột trong đối tượng này không cần có 767 cùng kiểu dữ liệu, và nếu hoàn toàn không muốn chỉ định một giá trị, bạn có thể đặt một cột 768 thành <code>null</code> bằng cách sử dụng {@link android.content.ContentValues#putNull ContentValues.putNull()}. 769</p> 770<p> 771 Đoạn mã HTML không thêm cột <code>_ID</code> vì cột này được tự động 772 duy trì. Trình cung cấp sẽ gán một giá trị duy nhất <code>_ID</code> cho mỗi hàng được 773 thêm. Các trình cung cấp thường sử dụng giá trị này làm khóa chính của bảng. 774</p> 775<p> 776 URI nội dung được trả về trong <code>newUri</code> sẽ xác định hàng mới thêm, có 777 định dạng như sau: 778</p> 779<pre> 780content://user_dictionary/words/<id_value> 781</pre> 782<p> 783 <code><id_value></code> là nội dung của <code>_ID</code> cho hàng mới. 784 Hầu hết các trình cung cấp đều có thể tự động phát hiện dạng URI nội dung này rồi thực hiện thao tác được yêu cầu 785 trên hàng cụ thể đó. 786</p> 787<p> 788 Để nhận giá trị <code>_ID</code> từ {@link android.net.Uri} được trả về, hãy gọi 789 {@link android.content.ContentUris#parseId ContentUris.parseId()}. 790</p> 791<h3 id="Updating">Cập nhật dữ liệu</h3> 792<p> 793 Để cập nhật một hàng, bạn sử dụng một đối tượng {@link android.content.ContentValues} với các giá trị 794 được cập nhật giống như cách bạn làm với việc chèn, và các tiêu chí lựa chọn giống như cách bạn làm với truy vấn. 795 Phương pháp máy khách mà bạn sử dụng là 796 {@link android.content.ContentResolver#update ContentResolver.update()}. Bạn chỉ cần thêm 797 các giá trị vào đối tượng {@link android.content.ContentValues} cho các cột mà bạn đang cập nhật. Nếu bạn 798 muốn xóa các nội dung của một cột, hãy đặt giá trị thành <code>null</code>. 799</p> 800<p> 801 Đoạn mã HTML sau thay đổi tất cả hàng với cột bản địa có ngôn ngữ "en" thành cột 802 có bản địa là <code>null</code>. Giá trị trả về là số hàng đã được cập nhật: 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 Bạn cũng nên thanh lọc thông tin đầu vào của người dùng khi gọi 831 {@link android.content.ContentResolver#update ContentResolver.update()}. Để tìm hiểu thêm về 832 điều này, hãy đọc phần <a href="#Injection">Bảo vệ trước mục nhập độc hại</a>. 833</p> 834<h3 id="Deleting">Xóa dữ liệu</h3> 835<p> 836 Xóa hàng tương tự như truy xuất dữ liệu hàng: bạn chỉ định các tiêu chí lựa chọn cho hàng 837 mà bạn muốn xóa và phương pháp máy khách trả về số hàng được xóa. 838 Đoạn mã HTML sau xóa các hàng có appid khớp với "user". Phương pháp sẽ trả về 839 số hàng được xóa. 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 Bạn cũng nên thanh lọc thông tin đầu vào của người dùng khi gọi 861 {@link android.content.ContentResolver#delete ContentResolver.delete()}. Để tìm hiểu thêm về 862 điều này, hãy đọc phần <a href="#Injection">Bảo vệ trước mục nhập độc hại</a>. 863</p> 864<!-- Provider Data Types --> 865<h2 id="DataTypes">Các Kiểu Dữ liệu của Trình cung cấp</h2> 866<p> 867 Trình cung cấp nội dung có thể cung cấp nhiều kiểu dữ liệu khác nhau. Trình cung cấp Từ điển Người dùng chỉ cung cấp 868 văn bản, nhưng trình cung cấp cũng có thể cung cấp các định dạng sau: 869</p> 870 <ul> 871 <li> 872 integer 873 </li> 874 <li> 875 long integer (long) 876 </li> 877 <li> 878 floating point 879 </li> 880 <li> 881 long floating point (double) 882 </li> 883 </ul> 884<p> 885 Một kiểu dữ liệu khác mà các trình cung cấp thường sử dụng đó là Binary Large OBject (BLOB) được triển khai như một 886 mảng 64KB byte. Bạn có thể xem các kiểu dữ liệu có sẵn bằng cách xem các phương pháp "get" lớp 887 {@link android.database.Cursor}. 888</p> 889<p> 890 Kiểu dữ liệu đối với mỗi cột trong một trình cung cấp thường được liệt kê trong tài liệu của trình cung cấp đó. 891 Các kiểu dữ liệu dành cho Trình cung cấp Từ điển Người dùng được liệt kê trong tài liệu tham khảo 892 cho lớp hợp đồng {@link android.provider.UserDictionary.Words} của nó (lớp hợp đồng được 893 mô tả trong phần <a href="#ContractClasses">Các Lớp Hợp đồng</a>). 894 Bạn cũng có thể xác định kiểu dữ liệu bằng cách gọi {@link android.database.Cursor#getType 895 Cursor.getType()}. 896</p> 897<p> 898 Trình cung cấp cũng duy trì thông tin về kiểu dữ liệu MIME cho mỗi URI nội dung mà chúng định nghĩa. Bạn có thể 899 sử dụng thông tin về kiểu MIME để tìm hiểu xem ứng dụng của mình có thể xử lý dữ liệu mà 900 trình cung cấp đưa ra hay không, hoặc để chọn một kiểu xử lý dựa trên kiểu MIME. Bạn thường cần kiểu 901 MIME khi đang làm việc với một trình cung cấp chứa các cấu trúc hoặc tệp 902 dữ liệu phức tạp. Ví dụ, bảng {@link android.provider.ContactsContract.Data} 903 trong Trình cung cấp Danh bạ sử dụng các kiểu MIME để dán nhãn kiểu dữ liệu liên lạc được lưu trữ trong từng 904 hàng. Để nhận được kiểu MIME tương ứng với một URI nội dung, hãy gọi 905 {@link android.content.ContentResolver#getType ContentResolver.getType()}. 906</p> 907<p> 908 Phần <a href="#MIMETypeReference">Tham khảo Kiểu MIME</a> mô tả 909 cú pháp của cả kiểu MIME tiêu chuẩn lẫn tùy chỉnh. 910</p> 911 912 913<!-- Alternative Forms of Provider Access --> 914<h2 id="AltForms">Các Hình thức Truy cập Trình cung cấp Thay thế</h2> 915<p> 916 Có ba hình thức truy cập trình cung cấp thay thế quan trọng trong phát triển ứng dụng: 917</p> 918<ul> 919 <li> 920 <a href="#Batch">Truy cập hàng loạt</a>: Bạn có thể tạo một loạt lệnh gọi truy cập bằng các phương pháp trong 921 lớp {@link android.content.ContentProviderOperation}, rồi sau đó áp dụng chúng với 922 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. 923 </li> 924 <li> 925 Truy vấn không đồng bộ: Bạn nên thực hiện các truy vấn trong một luồng riêng. Một cách để làm điều này đó là 926 sử dụng một đối tượng {@link android.content.CursorLoader}. Các ví dụ trong 927 hướng dẫn <a href="{@docRoot}guide/components/loaders.html">Trình tải</a> sẽ minh họa 928 cách làm điều này. 929 </li> 930 <li> 931 <a href="#Intents">Truy cập dữ liệu thông qua ý định</a>: Mặc dù không thể gửi một ý định 932 trực tiếp tới một trình cung cấp, bạn có thể gửi một ý định tới ứng dụng của trình cung cấp đó, 933 đây thường là cách tốt nhất để sửa đổi dữ liệu của trình cung cấp. 934 </li> 935</ul> 936<p> 937 Truy cập hàng loạt và sửa đổi thông qua ý định được mô tả trong các phần sau. 938</p> 939<h3 id="Batch">Truy cập hàng loạt</h3> 940<p> 941 Truy cập hàng loạt vào một trình cung cấp là cách hữu ích để chèn nhiều hàng, hoặc để chèn 942 các hàng vào nhiều bảng trong cùng lệnh gọi phương pháp, hoặc nhìn chung để thực hiện một tập hợp 943 thao tác qua các ranh giới tiến trình như một giao tác (thao tác nguyên tử). 944</p> 945<p> 946 Để truy cập một trình cung cấp trong "chế độ hàng loạt", 947 bạn tạo một mảng đối tượng {@link android.content.ContentProviderOperation} rồi 948 phân phối chúng tới một trình cung cấp nội dung bằng 949 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. Bạn chuyển 950 <em>quyền</em> của trình cung cấp nội dung cho phương pháp này thay vì một URI nội dung cụ thể. 951Điều này cho phép đối tượng {@link android.content.ContentProviderOperation} trong mảng có tác dụng 952 đối với một bảng khác. Một lệnh gọi tới {@link android.content.ContentResolver#applyBatch 953 ContentResolver.applyBatch()} trả về một mảng kết quả. 954</p> 955<p> 956 Mô tả lớp hợp đồng {@link android.provider.ContactsContract.RawContacts} 957 bao gồm một đoạn mã HTML thể hiện việc chèn hàng loạt. Ứng dụng mẫu 958 <a href="{@docRoot}resources/samples/ContactManager/index.html">Trình quản lý Danh bạ</a> 959 có một ví dụ về truy cập hàng loạt trong tệp nguồn <code>ContactAdder.java</code> 960 của nó. 961</p> 962<div class="sidebox-wrapper"> 963<div class="sidebox"> 964<h2>Hiển thị dữ liệu bằng cách sử dụng một ứng dụng trình trợ giúp.</h2> 965<p> 966 Nếu ứng dụng của bạn <em>có</em> quyền truy cập, bạn vẫn có thể cần sử dụng một 967 ý định để hiển thị dữ liệu trong một ứng dụng khác. Ví dụ, ứng dụng Lịch chấp nhận một 968 ý định {@link android.content.Intent#ACTION_VIEW}, nhằm hiển thị một ngày hoặc sự kiện cụ thể. 969 Điều này cho phép bạn hiển thị thông tin lịch mà không phải tạo UI của chính mình. 970 Để tìm hiểu thêm về tính năng này, hãy xem 971 hướng dẫn <a href="{@docRoot}guide/topics/providers/calendar-provider.html">Trình cung cấp Lịch</a>. 972</p> 973<p> 974 Ứng dụng mà bạn gửi ý định đến không nhất thiết phải là ứng dụng 975 được liên kết với trình cung cấp. Ví dụ, bạn có thể truy xuất một liên lạc từ 976 Trình cung cấp Danh bạ, rồi gửi một ý định {@link android.content.Intent#ACTION_VIEW} 977 chứa URI nội dung cho hình ảnh liên lạc tới một trình xem ảnh. 978</p> 979</div> 980</div> 981<h3 id="Intents">Truy cập dữ liệu thông qua ý định</h3> 982<p> 983 Ý định có thể cho phép truy cập gián tiếp vào một trình cung cấp nội dung. Bạn cho phép người dùng truy cập 984 dữ liệu trong một trình cung cấp ngay cả khi ứng dụng của bạn không có quyền truy cập, hoặc bằng cách 985 nhận lại một ý định kết quả từ một ứng dụng có quyền, hoặc bằng cách kích hoạt một 986 ứng dụng có phép và cho phép người dùng được làm việc trong nó. 987</p> 988<h4>Được truy cập với các quyền tạm thời</h4> 989<p> 990 Bạn có thể truy cập dữ liệu trong một trình cung cấp nội dung, ngay cả khi bạn không có quyền 991 truy nhập phù hợp, bằng cách gửi một ý định tới một ứng dụng có quyền và 992 nhận lại một ý định kết quả chứa quyền "URI". 993 Đây là những quyền cho một URI nội dung cụ thể kéo dài tới khi hoạt động nhận chúng 994 được hoàn thành. Ứng dụng có quyền lâu dài sẽ cấp quyền tạm thời 995 bằng cách đặt một cờ trong ý định kết quả: 996</p> 997<ul> 998 <li> 999 <strong>Quyền đọc:</strong> 1000 {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 1001 </li> 1002 <li> 1003 <strong>Quyền ghi:</strong> 1004 {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 1005 </li> 1006</ul> 1007<p class="note"> 1008 <strong>Lưu ý:</strong> Những cờ này không cấp quyền truy cập đọc và ghi nói chung cho trình cung cấp 1009 mà có quyền được chứa trong URI nội dung. Quyền truy cập này chỉ áp dụng cho chính URI đó. 1010</p> 1011<p> 1012 Trình cung cấp sẽ định nghĩa quyền URI cho các URI nội dung trong bản kê khai của nó, bằng cách sử dụng thuộc tính 1013<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code> 1014 của phần tử 1015<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 1016 cũng như phần tử con 1017<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></code> 1018 của phần tử 1019<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 1020 . Cơ chế cấp quyền URI này được giải thích chi tiết hơn trong 1021 hướng dẫn <a href="{@docRoot}guide/topics/security/security.html">Bảo mật và Quyền</a>, 1022 trong phần "Quyền URI". 1023</p> 1024<p> 1025 Ví dụ, bạn có thể truy xuất dữ liệu cho một liên lạc trong Trình cung cấp Danh bạ, ngay cả khi bạn không 1026 có quyền {@link android.Manifest.permission#READ_CONTACTS}. Bạn có thể muốn thực hiện điều này 1027 trong một ứng dụng gửi thiệp mừng điện tử tới một liên lạc vào ngày sinh nhật của người đó. Thay vì 1028 yêu cầu {@link android.Manifest.permission#READ_CONTACTS}, là nơi cấp cho bạn quyền truy cập tất cả liên lạc 1029 của người dùng và tất cả thông tin của họ, bạn nên cho phép người dùng kiểm soát những liên lạc 1030 nào được sử dụng bởi ứng dụng của bạn. Để làm điều này, bạn sử dụng tiến trình sau: 1031</p> 1032<ol> 1033 <li> 1034 Ứng dụng của bạn gửi một ý định chứa hành động 1035 {@link android.content.Intent#ACTION_PICK} và kiểu MIME "danh bạ" 1036{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}, bằng cách sử dụng 1037 phương pháp {@link android.app.Activity#startActivityForResult 1038 startActivityForResult()}. 1039 </li> 1040 <li> 1041 Vì ý định này khớp với bộ lọc ý định cho hoạt động 1042 "lựa chọn" của ứng dụng Danh bạ, hoạt động sẽ đi đến tiền cảnh. 1043 </li> 1044 <li> 1045 Trong hoạt động lựa chọn, người dùng chọn một 1046 liên lạc để cập nhật. Khi điều này xảy ra, hoạt động lựa chọn sẽ gọi 1047 {@link android.app.Activity#setResult setResult(resultcode, intent)} 1048 để thiết lập một ý định nhằm gửi lại ứng dụng của bạn. Ý định chứa URI nội dung 1049 của liên lạc mà người dùng đã chọn, và các cờ "phụ thêm" 1050 {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}. Những cờ này cấp quyền URI 1051 cho ứng dụng của bạn để đọc dữ liệu cho liên lạc được trỏ đến bởi 1052 URI nội dung. Sau đó, hoạt động lựa chọn gọi {@link android.app.Activity#finish()} để 1053 trả kiểm soát về ứng dụng của bạn. 1054 </li> 1055 <li> 1056 Hoạt động của bạn trả về tiền cảnh, và hệ thống sẽ gọi phương pháp 1057 {@link android.app.Activity#onActivityResult onActivityResult()} 1058 của hoạt động của bạn. Phương pháp này nhận được ý định kết quả do hoạt động lựa chọn tạo trong 1059 ứng dụng Danh bạ. 1060 </li> 1061 <li> 1062 Với URI nội dung từ ý định kết quả, bạn có thể đọc dữ liệu của liên lạc 1063 từ Trình cung cấp Danh bạ, ngay cả khi bạn không yêu cầu quyền truy cập đọc lâu dài 1064 vào trình cung cấp trong bản kê khai của mình. Sau đó, bạn có thể nhận thông tin ngày sinh của liên lạc 1065 hoặc địa chỉ e-mail của người đó rồi gửi thiệp mừng điện tử. 1066 </li> 1067</ol> 1068<h4>Sử dụng một ứng dụng khác</h4> 1069<p> 1070 Một cách đơn giản để cho phép người dùng sửa đổi dữ liệu mà bạn không có quyền truy cập đó là 1071 kích hoạt một ứng dụng có quyền và cho phép người dùng làm việc ở đó. 1072</p> 1073<p> 1074 Ví dụ, ứng dụng Lịch chấp nhận một 1075 ý định {@link android.content.Intent#ACTION_INSERT}, nó cho phép bạn kích hoạt UI chèn 1076 của ứng dụng. Bạn có thể chuyển dữ liệu "phụ thêm" trong ý định này mà được ứng dụng sử dụng 1077 để điền trước vào UI. Vì các sự kiện định kỳ có cú pháp phức tạp, cách 1078 ưu tiên để chèn sự kiện vào Trình cung cấp Lịch đó là kích hoạt ứng dụng Lịch với một 1079 {@link android.content.Intent#ACTION_INSERT} rồi để người dùng chèn sự kiện tại đó. 1080</p> 1081<!-- Contract Classes --> 1082<h2 id="ContractClasses">Các Lớp Hợp đồng</h2> 1083<p> 1084 Lớp hợp đồng định nghĩa các hằng số sẽ giúp ứng dụng hoạt động với các URI nội dung, tên 1085 cột, hành động ý định, và các tính năng khác của một trình cung cấp nội dung. Các lớp hợp đồng không 1086 được tự động đưa vào cùng một trình cung cấp; nhà phát triển của trình cung cấp phải định nghĩa chúng rồi 1087 cung cấp chúng cho các nhà phát triển khác. Nhiều trình cung cấp được bao gồm cùng với nền tảng 1088 Android có các lớp hợp đồng tương ứng trong gói {@link android.provider}. 1089</p> 1090<p> 1091 Ví dụ, Trình cung cấp Từ điển Người dùng có một lớp hợp đồng 1092 {@link android.provider.UserDictionary} chứa các hằng số URI nội dung và tên cột. URI nội dung 1093 đối với bảng "từ" được định nghĩa trong hằng số 1094 {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. 1095 Lớp {@link android.provider.UserDictionary.Words} cũng chứa các hằng số tên cột, 1096 chúng được sử dụng trong đoạn mã HTML mẫu trong hướng dẫn này. Ví dụ, một dự thảo truy vấn có thể được 1097 định nghĩa là: 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 Một lớp hợp đồng khác là {@link android.provider.ContactsContract} dành cho Trình cung cấp Danh bạ. 1109 Tài liệu tham khảo cho lớp này bao gồm các đoạn mã HTML mẫu. Một trong số các 1110 lớp con của nó, {@link android.provider.ContactsContract.Intents.Insert}, là một lớp hợp đồng 1111 chứa các hằng số cho ý định và dữ liệu ý định. 1112</p> 1113 1114 1115<!-- MIME Type Reference --> 1116<h2 id="MIMETypeReference">Tham khảo Kiểu MIME</h2> 1117<p> 1118 Trình cung cấp nội dung có thể trả về các kiểu phương tiện MIME tiêu chuẩn, hoặc xâu kiểu MIME tùy chỉnh, hoặc cả hai. 1119</p> 1120<p> 1121 Các kiểu MIME có định dạng 1122</p> 1123<pre> 1124<em>type</em>/<em>subtype</em> 1125</pre> 1126<p> 1127 Ví dụ, kiểu MIME thông dụng <code>text/html</code> có kiểu <code>text</code> và 1128 kiểu con <code>html</code>. Nếu trình cung cấp trả về loại này cho một URI, điều đó có nghĩa rằng một 1129 truy vấn đang sử dụng URI đó sẽ trả về văn bản chứa thẻ HTML. 1130</p> 1131<p> 1132 Các xâu kiểu MIME tùy chỉnh, còn gọi là kiểu MIME "theo nhà cung cấp", có các giá trị 1133 <em>kiểu</em> và <em>kiểu con</em> phức tạp hơn. Giá trị <em>kiểu</em> luôn luôn 1134</p> 1135<pre> 1136vnd.android.cursor.<strong>dir</strong> 1137</pre> 1138<p> 1139 áp dụng cho nhiều hàng, hoặc 1140</p> 1141<pre> 1142vnd.android.cursor.<strong>item</strong> 1143</pre> 1144<p> 1145 áp dụng cho một hàng. 1146</p> 1147<p> 1148 Giá trị <em>kiểu con</em> áp dụng theo trình cung cấp. Các trình cung cấp được tích hợp trong Android thường có một kiểu con 1149 đơn giản. Ví dụ, khi ứng dụng Danh bạ tạo một hàng cho một số điện thoại, 1150 nó đặt kiểu MIME sau trong hàng: 1151</p> 1152<pre> 1153vnd.android.cursor.item/phone_v2 1154</pre> 1155<p> 1156 Để ý rằng giá trị kiểu con đơn giản là <code>phone_v2</code>. 1157</p> 1158<p> 1159 Các nhà phát triển trình cung cấp khác có thể tạo mẫu hình kiểu con của riêng mình dựa trên quyền 1160 và tên bảng của trình cung cấp. Ví dụ, xét một trình cung cấp chứa các biểu thời gian lịch tàu. 1161 Quyền của trình cung cấp là <code>com.example.trains</code>, và nó chứa các bảng 1162 Line1, Line2, và Line3. Để phản hồi lại URI nội dung 1163</p> 1164<p> 1165<pre> 1166content://com.example.trains/Line1 1167</pre> 1168<p> 1169 đối với bảng Line1, trình cung cấp trả về kiểu MIME 1170</p> 1171<pre> 1172vnd.android.cursor.<strong>dir</strong>/vnd.example.line1 1173</pre> 1174<p> 1175 Để phản hồi lại URI nội dung 1176</p> 1177<pre> 1178content://com.example.trains/Line2/5 1179</pre> 1180<p> 1181 đối với hàng 5 trong bảng Line2, trình cung cấp trả về kiểu MIME 1182</p> 1183<pre> 1184vnd.android.cursor.<strong>item</strong>/vnd.example.line2 1185</pre> 1186<p> 1187 Hầu hết các trình cung cấp nội dung đều định nghĩa hằng số lớp hợp đồng cho các kiểu MIME mà chúng sử dụng. Ví dụ như lớp hợp đồng 1188 của Trình cung cấp Danh bạ {@link android.provider.ContactsContract.RawContacts}, 1189 sẽ định nghĩa hằng số 1190 {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} cho kiểu MIME của 1191 một hàng liên lạc thô duy nhất. 1192</p> 1193<p> 1194 Các URI nội dung đối với hàng duy nhất được mô tả trong phần 1195 <a href="#ContentURIs">URI nội dung</a>. 1196</p> 1197