Trình cung cấp nội dung quản lý quyền truy cập vào kho lưu trữ dữ liệu trung tâm. Một nhà cung cấp là một phần của ứng dụng Android, thường cung cấp giao diện người dùng riêng để làm việc với dữ liệu. Tuy nhiên, trình cung cấp nội dung chủ yếu được các bên khác các ứng dụng này truy cập trình cung cấp bằng cách sử dụng đối tượng ứng dụng nhà cung cấp. Các nhà cung cấp và cung cấp một giao diện chuẩn, nhất quán cho dữ liệu, đồng thời xử lý giao tiếp liên quy trình và bảo mật truy cập dữ liệu.
Thông thường, bạn sẽ làm việc với nhà cung cấp nội dung ở một trong hai tình huống: triển khai để truy cập trình cung cấp nội dung hiện có trong một ứng dụng khác hoặc tạo trình cung cấp nội dung mới trong ứng dụng của bạn để chia sẻ dữ liệu với các ứng dụng khác.
Trang này bao gồm những kiến thức cơ bản về cách hợp tác với các nhà cung cấp nội dung hiện tại. Để tìm hiểu về cách triển khai nhà cung cấp nội dung trong ứng dụng của riêng bạn, hãy xem Tạo một trình cung cấp nội dung.
Chủ đề này mô tả những nội dung sau:
- Cách hoạt động của trình cung cấp nội dung.
- API bạn sử dụng để truy xuất dữ liệu từ trình cung cấp nội dung.
- API mà bạn sử dụng để chèn, cập nhật hoặc xoá dữ liệu trong một trình cung cấp nội dung.
- Các tính năng API khác hỗ trợ hợp tác với nhà cung cấp.
Tổng quan
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ự như các bảng trong cơ sở dữ liệu quan hệ. Hàng đại diện cho một thực thể thuộc loại nào đó dữ liệu mà nhà cung cấp thu thập và mỗi cột trong hàng đại diện cho một phần dữ liệu đã thu thập cho một đối tượng nào đó.
Nhà cung cấp nội dung điều phối quyền truy cập vào lớp lưu trữ dữ liệu trong ứng dụng của bạn cho một số lượng API và thành phần khác nhau. Như minh hoạ trong Hình 1, các chỉ số này bao gồm:
- Chia sẻ quyền truy cập vào dữ liệu ứng dụng của bạn với các ứng dụng khác
- Gửi dữ liệu đến một tiện ích
- Trả về đề xuất tìm kiếm tùy chỉnh cho ứng dụng của bạn thông qua tìm kiếm
khung bằng
SearchRecentSuggestionsProvider
- Đồng bộ hoá dữ liệu ứng dụng với máy chủ của bạn bằng cách triển khai
AbstractThreadedSyncAdapter
- Tải dữ liệu trong giao diện người dùng bằng
CursorLoader
Truy cập vào một nhà cung cấp
Khi muốn truy cập dữ liệu trong một trình cung cấp nội dung, bạn cần sử dụng
Đối tượng ContentResolver
trong ứng dụng của bạn
Context
để giao tiếp với nhà cung cấp trong vai trò một khách hàng. Chiến lược phát hành đĩa đơn
Đối tượng ContentResolver
giao tiếp với đối tượng ứng dụng nhà cung cấp, một đối tượng
thực thể của một lớp triển khai ContentProvider
.
Nhà cung cấp
đối tượng nhận yêu cầu dữ liệu từ máy khách, thực hiện hành động được yêu cầu và trả về
kết quả. Đối tượng này có các phương thức gọi các phương thức có tên giống hệt nhau trong đối tượng trình cung cấp,
một thực thể của một trong các lớp con cụ thể của ContentProvider
. Chiến lược phát hành đĩa đơn
Phương thức ContentResolver
cung cấp thông tin cơ bản
"CRUD" Các chức năng (tạo, truy xuất, cập nhật và xoá) của bộ nhớ liên tục.
Một mẫu chung để truy cập ContentProvider
từ giao diện người dùng là sử dụng
CursorLoader
để chạy truy vấn không đồng bộ trong nền. Chiến lược phát hành đĩa đơn
Activity
hoặc Fragment
trong giao diện người dùng sẽ gọi một
CursorLoader
cho truy vấn, từ đó nhận được
ContentProvider
bằng ContentResolver
.
Điều này cho phép người dùng tiếp tục truy cập vào giao diện người dùng trong khi truy vấn đang chạy. Chiến dịch này liên quan đến sự tương tác của một số đối tượng khác nhau cũng như cơ chế cơ chế lưu trữ, như minh hoạ trong Hình 2.
Lưu ý: Để truy cập vào một nhà cung cấp, ứng dụng của bạn thường phải yêu cầu một số thông tin trong tệp kê khai. Mẫu phát triển này được mô tả chi tiết hơn trong Quyền của trình cung cấp nội dung.
Một trong những nhà cung cấp tích hợp sẵn trong nền tảng Android là Nhà cung cấp từ điển người dùng. sẽ lưu trữ những từ không theo chuẩn mà người dùng muốn giữ lại. Bảng 1 minh hoạ những gì dữ liệu có thể trông giống như trong bảng của nhà cung cấp này:
word | id ứng dụng | đăng video | ngôn ngữ | _Mã |
---|---|---|---|---|
mapreduce |
người dùng1 | 100 | vi_VN | 1 |
precompiler |
người dùng 14 | 200 | fr_FR | 2 |
applet |
người dùng2 | 225 | fr_CA | 3 |
const |
người dùng1 | 255 | pt_BR | 4 |
int |
người dùng 5 | 100 | vi_VN | 5 |
Trong bảng 1, mỗi hàng đại diện cho một phiên bản của một từ không
có trong từ điển chuẩn. Mỗi cột đại diện cho một phần dữ liệu của từ đó, chẳng hạn như
ngôn ngữ nơi gặp mã đó lần đầu. Tiêu đề cột là tên cột được lưu trữ trong
cho nhà cung cấp. Vì vậy, để tham chiếu đến ngôn ngữ của một hàng, bạn sẽ tham chiếu đến cột locale
của hàng đó. Cho
nhà cung cấp này, cột _ID
đóng vai trò là cột khoá chính
mà nhà cung cấp tự động duy trì.
Để nhận danh sách các từ và ngôn ngữ của chúng từ Nhà cung cấp từ điển người dùng,
bạn gọi ContentResolver.query()
.
Phương thức query()
gọi phương thức
ContentProvider.query()
được xác định bởi
Nhà cung cấp từ điển người dùng. Các dòng mã sau đây cho thấy
Cuộc gọi ContentResolver.query()
:
Kotlin
// Queries the UserDictionary and returns results cursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs.toTypedArray(), // Selection criteria sortOrder // The sort order for the returned rows )
Java
// Queries the UserDictionary and returns results cursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Selection criteria selectionArgs, // Selection criteria sortOrder); // The sort order for the returned rows
Bảng 2 cho thấy cách các đối số để
query(Uri,projection,selection,selectionArgs,sortOrder)
khớp với câu lệnh SELECT SQL:
Đối số query() |
SELECT từ khóa/thông số | Ghi chú |
---|---|---|
Uri |
FROM table_name |
Uri liên kết với bảng trong trình cung cấp có tên là table_name. |
projection |
col,col,col,... |
projection là một mảng cột được đưa vào mỗi hàng
đã truy xuất.
|
selection |
WHERE col = value |
selection chỉ định tiêu chí để chọn hàng. |
selectionArgs |
Không có thông tin tương đương chính xác. Đối số lựa chọn thay thế phần giữ chỗ ? trong phần
mệnh đề lựa chọn.
|
|
sortOrder |
ORDER BY col,col,... |
sortOrder chỉ định thứ tự các hàng sẽ xuất hiện trong phần tử được trả về
Cursor
|
URI nội dung
URI nội dung là một URI xác định dữ liệu trong một trình cung cấp. URI nội dung bao gồm tên tượng trưng của toàn bộ nhà cung cấp – cơ quan của nhà cung cấp đó – và tên trỏ đến một bảng – một đường dẫn. Khi bạn gọi một phương thức ứng dụng khách để truy cập vào bảng trong một nhà cung cấp, URI nội dung cho bảng là một trong các đối số.
Trong các dòng mã trước, hằng số
CONTENT_URI
chứa URI nội dung của
bảng Words
của Nhà cung cấp từ điển người dùng. ContentResolver
đối tượng phân tích cú pháp quyền của URI và sử dụng quyền này để giải quyết trình cung cấp bằng cách
so sánh đơn vị có thẩm quyền với một bảng hệ thống gồm các nhà cung cấp đã biết. Chiến lược phát hành đĩa đơn
Sau đó, ContentResolver
có thể gửi đối số truy vấn đến đúng
Google Cloud.
ContentProvider
sử dụng phần đường dẫn của URI nội dung để chọn
bảng để truy cập. Nhà cung cấp thường có một đường dẫn cho mỗi bảng mà nó hiển thị.
Trong các dòng mã trước, URI đầy đủ cho bảng Words
là:
content://user_dictionary/words
- Chuỗi
content://
là giản đồ và luôn hiện diện và xác định đây là URI nội dung. - Chuỗi
user_dictionary
là quyền của trình cung cấp. - Chuỗi
words
là đường dẫn của bảng.
Nhiều nhà cung cấp cho phép bạn truy cập vào một hàng trong bảng bằng cách thêm giá trị mã nhận dạng
vào cuối URI. Ví dụ: để truy xuất một hàng có _ID
là
4
từ Nhà cung cấp từ điển người dùng, bạn có thể sử dụng URI nội dung này:
Kotlin
val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)
Java
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Bạn thường sử dụng giá trị mã nhận dạng khi truy xuất một tập hợp các hàng và sau đó muốn cập nhật hoặc xoá một trong số đó.
Lưu ý: Các lớp Uri
và Uri.Builder
chứa các phương thức tiện lợi để tạo đối tượng URI được định dạng đúng từ các chuỗi. Chiến lược phát hành đĩa đơn
Lớp ContentUris
chứa các phương thức tiện lợi để thêm giá trị mã nhận dạng vào
URI. Đoạn mã trước sử dụng withAppendedId()
để nối mã nhận dạng vào URI nội dung của Nhà cung cấp từ điển người dùng.
Truy xuất dữ liệu từ nhà cung cấp
Phần này mô tả cách truy xuất dữ liệu từ nhà cung cấp, sử dụng Nhà cung cấp từ điển người dùng làm ví dụ.
Để cho rõ ràng, các đoạn mã trong phần này gọi
ContentResolver.query()
trên luồng giao diện người dùng. Trong
mã thực tế, tuy nhiên, truy vấn không đồng bộ trên một luồng riêng. Bạn có thể
sử dụng lớp CursorLoader
, được mô tả
chi tiết hơn trong
Hướng dẫn về trình tải. Ngoài ra, các dòng mã chỉ là đoạn mã. Chúng không hiển thị đầy đủ
.
Để truy xuất dữ liệu từ một nhà cung cấp, hãy làm theo các bước cơ bản sau:
- Hãy yêu cầu nhà cung cấp cấp quyền đọc.
- Xác định mã sẽ gửi truy vấn đến trình cung cấp.
Yêu cầu cấp quyền đọc
Để truy xuất dữ liệu từ một nhà cung cấp, ứng dụng của bạn cần có quyền đọc đối với
Google Cloud. 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
bạn cần quyền này trong tệp kê khai, bằng cách sử dụng
<uses-permission>
và tên quyền chính xác được xác định bởi
Google Cloud.
Khi chỉ định phần tử này trong tệp kê khai, bạn sẽ yêu cầu phần tử này cho ứng dụng của bạn. Khi người dùng cài đặt ứng dụng của bạn, họ ngầm cấp cho yêu cầu này.
Để tìm tên chính xác của quyền truy cập đọc của nhà cung cấp mà bạn đang sử dụng, dưới dạng tên cho các quyền truy cập khác mà nhà cung cấp sử dụng, hãy xem tài liệu.
Vai trò của quyền trong việc truy cập vào nhà cung cấp được mô tả chi tiết hơn trong Quyền của trình cung cấp nội dung.
Trình cung cấp từ điển người dùng xác định quyền
android.permission.READ_USER_DICTIONARY
trong tệp kê khai, vì vậy,
ứng dụng muốn đọc từ nhà cung cấp phải yêu cầu quyền này.
Tạo truy vấn
Bước tiếp theo trong việc truy xuất dữ liệu từ nhà cung cấp là tạo một truy vấn. Đoạn mã sau xác định một số biến để truy cập vào Nhà cung cấp từ điển người dùng:
Kotlin
// A "projection" defines the columns that are returned for each row private val mProjection: Array<String> = arrayOf( UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name ) // Defines a string to contain the selection clause private var selectionClause: String? = null // Declares an array to contain selection arguments private lateinit var selectionArgs: Array<String>
Java
// A "projection" defines the columns that are returned for each row String[] mProjection = { UserDictionary.Words._ID, // Contract class constant for the _ID column name UserDictionary.Words.WORD, // Contract class constant for the word column name UserDictionary.Words.LOCALE // Contract class constant for the locale column name }; // Defines a string to contain the selection clause String selectionClause = null; // Initializes an array to contain selection arguments String[] selectionArgs = {""};
Đoạn mã tiếp theo cho biết cách sử dụng
ContentResolver.query()
, sử dụng Từ điển người dùng
Nhà cung cấp là ví dụ. Truy vấn ứng dụng của nhà cung cấp cũng tương tự như truy vấn SQL và chứa
tập hợp cột cần trả về, một tập hợp các tiêu chí chọn và thứ tự sắp xếp.
Tập hợp các cột mà truy vấn trả về được gọi là phép chiếu và
biến là mProjection
.
Biểu thức chỉ định các hàng cần truy xuất được chia thành mệnh đề lựa chọn và
đối số lựa chọn. Mệnh đề lựa chọn là sự kết hợp giữa biểu thức logic và boolean,
tên cột và giá trị cột. Biến này là mSelectionClause
. Nếu bạn chỉ định
tham số có thể thay thế ?
thay vì một giá trị, phương thức truy vấn sẽ truy xuất giá trị
từ mảng đối số lựa chọn, chính là biến mSelectionArgs
.
Trong đoạn mã tiếp theo, nếu người dùng không nhập từ, mệnh đề lựa chọn sẽ được đặt thành
null
và truy vấn trả về tất cả các từ có trong trình cung cấp. Nếu người dùng nhập
một từ, mệnh đề lựa chọn được đặt thành UserDictionary.Words.WORD + " = ?"
và
phần tử đầu tiên của mảng đối số lựa chọn được đặt thành từ mà người dùng nhập.
Kotlin
/* * This declares a String array to contain the selection arguments. */ private lateinit var selectionArgs: Array<String> // Gets a word from the UI searchString = searchWord.text.toString() // Insert code here to check for invalid or malicious input // If the word is the empty string, gets everything selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let { selectionClause = "${UserDictionary.Words.WORD} = ?" arrayOf(it) } ?: run { selectionClause = null emptyArray<String>() } // Does a query against the table and returns a Cursor object mCursor = contentResolver.query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder // The sort order for the returned rows ) // Some providers return null if an error occurs, others throw an exception when (mCursor?.count) { null -> { /* * Insert code here to handle the error. Be sure not to use the cursor! * You might want to call android.util.Log.e() to log this error. */ } 0 -> { /* * Insert code here to notify the user that the search is unsuccessful. This isn't * necessarily an error. You might want to offer the user the option to insert a new * row, or re-type the search term. */ } else -> { // Insert code here to do something with the results } }
Java
/* * This defines a one-element String array to contain the selection argument. */ String[] selectionArgs = {""}; // Gets a word from the UI searchString = searchWord.getText().toString(); // Remember to insert code here to check for invalid or malicious input // If the word is the empty string, gets everything if (TextUtils.isEmpty(searchString)) { // Setting the selection clause to null returns all words selectionClause = null; selectionArgs[0] = ""; } else { // Constructs a selection clause that matches the word that the user entered selectionClause = UserDictionary.Words.WORD + " = ?"; // Moves the user's input string to the selection arguments selectionArgs[0] = searchString; } // Does a query against the table and returns a Cursor object mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table projection, // The columns to return for each row selectionClause, // Either null or the word the user entered selectionArgs, // Either empty or the string the user entered sortOrder); // The sort order for the returned rows // Some providers return null if an error occurs, others throw an exception if (null == mCursor) { /* * Insert code here to handle the error. Be sure not to use the cursor! You can * call android.util.Log.e() to log this error. * */ // If the Cursor is empty, the provider found no matches } else if (mCursor.getCount() < 1) { /* * Insert code here to notify the user that the search is unsuccessful. This isn't necessarily * an error. You can offer the user the option to insert a new row, or re-type the * search term. */ } else { // Insert code here to do something with the results }
Truy vấn này tương tự như câu lệnh SQL sau:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
Trong câu lệnh SQL này, tên cột thực tế sẽ được dùng thay cho hằng số lớp hợp đồng.
Bảo vệ khỏi dữ liệu đầu vào độc hại
Nếu dữ liệu do trình cung cấp nội dung quản lý nằm trong cơ sở dữ liệu SQL, bao gồm cả dữ liệu không đáng tin cậy bên ngoài vào câu lệnh SQL thô có thể dẫn đến việc chèn SQL.
Hãy xem xét mệnh đề lựa chọn sau:
Kotlin
// Constructs a selection clause by concatenating the user's input to the column name var selectionClause = "var = $mUserInput"
Java
// Constructs a selection clause by concatenating the user's input to the column name String selectionClause = "var = " + userInput;
Nếu làm như vậy, bạn sẽ cho phép người dùng ghép nối SQL độc hại vào câu lệnh SQL của bạn.
Ví dụ: người dùng có thể nhập "nothing; BẢNG THẤP *;" cho mUserInput
, tức là
cho kết quả trong mệnh đề lựa chọn var = nothing; DROP TABLE *;
.
Vì Mệnh đề lựa chọn được coi là câu lệnh SQL, điều này có thể khiến trình cung cấp xoá tất cả các bảng trong cơ sở dữ liệu SQLite cơ bản, trừ phi trình cung cấp được thiết lập để nắm bắt Chèn SQL.
Để tránh vấn đề này, hãy sử dụng mệnh đề lựa chọn sử dụng ?
làm thành phần có thể thay thế
và một mảng các đối số lựa chọn riêng biệt. Bằng cách này, thông tin đầu vào của người dùng
được liên kết trực tiếp với truy vấn thay vì được hiểu như một phần của câu lệnh SQL.
Vì không được xem là SQL nên hoạt động đầu vào của người dùng không thể chèn SQL độc hại. Thay vì sử dụng
nối để bao gồm hoạt động đầu vào của người dùng, hãy sử dụng mệnh đề lựa chọn sau:
Kotlin
// Constructs a selection clause with a replaceable parameter var selectionClause = "var = ?"
Java
// Constructs a selection clause with a replaceable parameter String selectionClause = "var = ?";
Thiết lập mảng các đối số lựa chọn như sau:
Kotlin
// Defines a mutable list to contain the selection arguments var selectionArgs: MutableList<String> = mutableListOf()
Java
// Defines an array to contain the selection arguments String[] selectionArgs = {""};
Đặt một giá trị vào mảng đối số lựa chọn như sau:
Kotlin
// Adds the user's input to the selection argument selectionArgs += userInput
Java
// Sets the selection argument to the user's input selectionArgs[0] = userInput;
Mệnh đề lựa chọn sử dụng ?
làm tham số có thể thay thế và một mảng
Mảng đối số lựa chọn là cách ưu tiên để chỉ định lựa chọn, ngay cả khi đối số cung cấp không phải là
dựa trên cơ sở dữ liệu SQL.
Hiển thị kết quả truy vấn
Phương thức ứng dụng ContentResolver.query()
luôn
trả về một Cursor
chứa các cột được chỉ định bởi
phép chiếu cho các hàng phù hợp với tiêu chí lựa chọn của truy vấn. Đáp
Đối tượng Cursor
cung cấp quyền đọc ngẫu nhiên vào các hàng và cột đối tượng đó
chứa.
Khi sử dụng các phương thức Cursor
, bạn có thể lặp lại các hàng trong phần tử
kết quả, xác định loại dữ liệu của mỗi cột, lấy dữ liệu ra khỏi cột và tìm hiểu các dữ liệu khác
thuộc tính của kết quả.
Một số quá trình triển khai Cursor
tự động
cập nhật đối tượng khi dữ liệu của trình cung cấp thay đổi, các phương thức kích hoạt trong đối tượng tiếp nhận dữ liệu
khi Cursor
thay đổi hoặc cả hai.
Lưu ý: Nhà cung cấp có thể hạn chế quyền truy cập vào các cột tuỳ theo bản chất của tạo truy vấn. Ví dụ: Trình cung cấp danh bạ hạn chế quyền truy cập của một số cột để các bộ điều hợp đồng bộ hoá để không trả về chúng cho một hoạt động hoặc dịch vụ.
Nếu không có hàng nào khớp với tiêu chí lựa chọn, thì trình cung cấp
sẽ trả về đối tượng Cursor
Cursor.getCount()
là
0 – tức là con trỏ trống.
Nếu xảy ra lỗi nội bộ, kết quả của truy vấn phụ thuộc vào nhà cung cấp cụ thể. Có thể
trả về null
hoặc có thể gửi một Exception
.
Vì Cursor
là một danh sách các hàng nên một cách hay để hiển thị
nội dung của Cursor
là liên kết nó với một ListView
bằng SimpleCursorAdapter
.
Đoạn mã sau đây sẽ tiếp tục mã của đoạn mã trước đó. Chiến dịch này tạo ra
Đối tượng SimpleCursorAdapter
chứa Cursor
được truy xuất bởi truy vấn và đặt đối tượng này làm bộ chuyển đổi cho
ListView
.
Kotlin
// Defines a list of columns to retrieve from the Cursor and load into an output row val wordListColumns : Array<String> = arrayOf( UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name ) // Defines a list of View IDs that receive the Cursor columns for each row val wordListItems = intArrayOf(R.id.dictWord, R.id.locale) // Creates a new SimpleCursorAdapter cursorAdapter = SimpleCursorAdapter( applicationContext, // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0 // Flags (usually none are needed) ) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter)
Java
// Defines a list of columns to retrieve from the Cursor and load into an output row String[] wordListColumns = { UserDictionary.Words.WORD, // Contract class constant containing the word column name UserDictionary.Words.LOCALE // Contract class constant containing the locale column name }; // Defines a list of View IDs that receive the Cursor columns for each row int[] wordListItems = { R.id.dictWord, R.id.locale}; // Creates a new SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter( getApplicationContext(), // The application's Context object R.layout.wordlistrow, // A layout in XML for one row in the ListView mCursor, // The result from the query wordListColumns, // A string array of column names in the cursor wordListItems, // An integer array of view IDs in the row layout 0); // Flags (usually none are needed) // Sets the adapter for the ListView wordList.setAdapter(cursorAdapter);
Lưu ý: Để sao lưu ListView
bằng
Cursor
thì con trỏ phải chứa cột có tên _ID
.
Do đó, truy vấn đã hiển thị trước đó sẽ truy xuất cột _ID
cho thuộc tính
Bảng Words
, mặc dù ListView
không hiển thị bảng đó.
Quy định hạn chế này cũng giải thích lý do hầu hết các nhà cung cấp đều có một cột _ID
cho mỗi
bảng của họ.
Lấy dữ liệu từ kết quả truy vấn
Ngoài việc hiển thị kết quả truy vấn, bạn có thể sử dụng các kết quả này cho các tác vụ khác. Cho
ví dụ: bạn có thể truy xuất cách viết từ Nhà cung cấp từ điển người dùng rồi tra cứu trong
các nhà cung cấp khác. Để thực hiện việc này, bạn lặp lại các hàng trong Cursor
, như trong ví dụ sau:
Kotlin
/* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ mCursor?.apply { // Determine the column index of the column named "word" val index: Int = getColumnIndex(UserDictionary.Words.WORD) /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (moveToNext()) { // Gets the value from the column newWord = getString(index) // Insert code here to process the retrieved word ... // End of while loop } }
Java
// Determine the column index of the column named "word" int index = mCursor.getColumnIndex(UserDictionary.Words.WORD); /* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers might throw an Exception instead of returning null. */ if (mCursor != null) { /* * Moves to the next row in the cursor. Before the first movement in the cursor, the * "row pointer" is -1, and if you try to retrieve data at that position you get an * exception. */ while (mCursor.moveToNext()) { // Gets the value from the column newWord = mCursor.getString(index); // Insert code here to process the retrieved word ... // End of while loop } } else { // Insert code here to report an error if the cursor is null or the provider threw an exception }
Quá trình triển khai Cursor
chứa một số lệnh "get" phương thức cho
truy xuất các loại dữ liệu khác nhau từ đối tượng. Ví dụ: đoạn mã trước
sử dụng getString()
. Chúng cũng có
Phương thức getType()
trả về một giá trị cho biết
loại dữ liệu của cột.
Phát hành tài nguyên kết quả truy vấn
Cursor
đối tượng phải là
đóng nếu chúng không cần thiết nữa để các tài nguyên liên kết với chúng được giải phóng
sớm hơn. Bạn có thể thực hiện việc này bằng cách gọi
close()
hoặc bằng cách sử dụng
câu lệnh try-with-resources
trong ngôn ngữ lập trình Java hoặc
Hàm use()
trong ngôn ngữ lập trình Kotlin.
Quyền của trình cung cấp nội dung
Ứng dụng của nhà cung cấp có thể chỉ định các quyền mà các ứng dụng khác phải có truy cập vào dữ liệu của nhà cung cấp. Các quyền này cho người dùng biết dữ liệu nào một ứng dụng cố gắng truy cập. Dựa trên yêu cầu của nhà cung cấp, các ứng dụng khác yêu cầu các quyền họ cần để truy cập nhà cung cấp. Người dùng cuối sẽ thấy khi cài đặt ứng dụng.
Nếu ứng dụng của nhà cung cấp không chỉ định bất kỳ quyền nào thì các ứng dụng khác sẽ không có quyền truy cập vào dữ liệu của nhà cung cấp, trừ phi nhà cung cấp được xuất. Ngoài ra, các thành phần trong ứng dụng của nhà cung cấp luôn có quyền đọc và ghi đầy đủ, bất kể quyền đã chỉ định.
Nhà cung cấp từ điển người dùng yêu cầu
Quyền của android.permission.READ_USER_DICTIONARY
để truy xuất dữ liệu qua đó.
Nhà cung cấp có một android.permission.WRITE_USER_DICTIONARY
riêng
quyền chèn, cập nhật hoặc xoá dữ liệu.
Để có được các quyền cần thiết để truy cập vào một nhà cung cấp, một ứng dụng sẽ yêu cầu họ bằng một
<uses-permission>
trong tệp kê khai. Khi Android Package Manager (Trình quản lý gói Android) cài đặt ứng dụng, người dùng
phải phê duyệt tất cả các quyền mà ứng dụng yêu cầu. Nếu người dùng phê duyệt,
Trình quản lý gói sẽ tiếp tục cài đặt. Nếu người dùng không phê duyệt, Trình quản lý gói
hãy dừng cài đặt.
Mẫu sau đây
<uses-permission>
phần tử yêu cầu quyền đọc đối với Nhà cung cấp từ điển người dùng:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Tác động của các quyền đối với quyền truy cập của nhà cung cấp được giải thích chi tiết hơn trong Mẹo bảo mật.
Chèn, cập nhật và xoá dữ liệu
Giống như cách truy xuất dữ liệu từ một nhà cung cấp, bạn cũng sử dụng tương tác giữa
ứng dụng của nhà cung cấp và ContentProvider
của nhà cung cấp để sửa đổi dữ liệu.
Bạn gọi một phương thức của ContentResolver
với các đối số được truyền đến
phương thức tương ứng của ContentProvider
. Nhà cung cấp
ứng dụng khách tự động xử lý vấn đề bảo mật và giao tiếp liên quy trình.
Chèn dữ liệu
Để chèn dữ liệu vào một nhà cung cấp, bạn gọi hàm
ContentResolver.insert()
. Phương thức này sẽ 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 đó.
Đoạn mã sau đâ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:
Kotlin
// Defines a new Uri object that receives the result of the insertion lateinit var newUri: Uri ... // Defines an object to contain the new values to insert val newValues = ContentValues().apply { /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ put(UserDictionary.Words.APP_ID, "example.user") put(UserDictionary.Words.LOCALE, "en_US") put(UserDictionary.Words.WORD, "insert") put(UserDictionary.Words.FREQUENCY, "100") } newUri = contentResolver.insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert )
Java
// Defines a new Uri object that receives the result of the insertion Uri newUri; ... // Defines an object to contain the new values to insert ContentValues newValues = new ContentValues(); /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value". */ newValues.put(UserDictionary.Words.APP_ID, "example.user"); newValues.put(UserDictionary.Words.LOCALE, "en_US"); newValues.put(UserDictionary.Words.WORD, "insert"); newValues.put(UserDictionary.Words.FREQUENCY, "100"); newUri = getContentResolver().insert( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI newValues // The values to insert );
Dữ liệu cho hàng mới sẽ chuyển vào một đối tượng ContentValues
duy nhất. Điều này
có dạng tương tự như con trỏ một hàng. Các cột trong đối tượng này không cần phải có
cùng một loại dữ liệu và nếu bạn không muốn chỉ định một giá trị, bạn có thể đặt cột
đến null
bằng ContentValues.putNull()
.
Đoạn mã trước không thêm cột _ID
vì cột này vẫn được duy trì
tự động. Nhà cung cấp này chỉ định một giá trị duy nhất là _ID
cho mỗi hàng
đã thêm. Nhà cung cấp thường sử dụng giá trị này làm khoá chính của bảng.
URI nội dung được trả về trong newUri
xác định hàng mới được thêm chứa
dưới định dạng sau:
content://user_dictionary/words/<id_value>
<id_value>
là nội dung của _ID
cho hàng mới.
Hầu hết các nhà cung cấp đều có thể tự động phát hiện dạng URI nội dung này, sau đó thực hiện yêu cầu
trên hàng cụ thể đó.
Để nhận giá trị của _ID
từ Uri
được trả về, hãy gọi
ContentUris.parseId()
.
Cập nhật dữ liệu
Để cập nhật một hàng, bạn sử dụng đối tượng ContentValues
với thuộc tính
các giá trị, giống như cách bạn thực hiện với tiêu chí chèn và lựa chọn, như cách bạn thực hiện với truy vấn.
Phương thức ứng dụng bạn sử dụng là
ContentResolver.update()
. Bạn chỉ cần thêm
vào đối tượng ContentValues
cho các cột mà bạn đang cập nhật. Nếu bạn
muốn xoá nội dung của cột, hãy đặt giá trị thành null
.
Đoạn mã sau đây thay đổi tất cả các hàng có ngôn ngữ có ngôn ngữ "en"
thành
có ngôn ngữ null
. Giá trị trả về là số hàng đã được cập nhật.
Kotlin
// Defines an object to contain the updated values val updateValues = ContentValues().apply { /* * Sets the updated value and updates the selected words. */ putNull(UserDictionary.Words.LOCALE) } // Defines selection criteria for the rows you want to update val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?" val selectionArgs: Array<String> = arrayOf("en_%") // Defines a variable to contain the number of updated rows var rowsUpdated: Int = 0 ... rowsUpdated = contentResolver.update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines an object to contain the updated values ContentValues updateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String selectionClause = UserDictionary.Words.LOCALE + " LIKE ?"; String[] selectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int rowsUpdated = 0; ... /* * Sets the updated value and updates the selected words. */ updateValues.putNull(UserDictionary.Words.LOCALE); rowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI updateValues, // The columns to update selectionClause, // The column to select on selectionArgs // The value to compare to );
Dọn dẹp hoạt động đầu vào của người dùng khi bạn gọi
ContentResolver.update()
. Để tìm hiểu thêm về
hãy đọc phần Bảo vệ khỏi dữ liệu nhập độc hại.
Xoá dữ liệu
Xoá hàng tương tự như truy xuất dữ liệu hàng. Bạn chỉ định tiêu chí lựa chọn cho các hàng
bạn muốn xoá và phương thức ứng dụng trả về số hàng đã xoá.
Đoạn mã sau đây sẽ xoá các hàng có mã ứng dụng khớp với "user"
. Phương thức này trả về
số hàng đã xoá.
Kotlin
// Defines selection criteria for the rows you want to delete val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?" val selectionArgs: Array<String> = arrayOf("user") // Defines a variable to contain the number of rows deleted var rowsDeleted: Int = 0 ... // Deletes the words that match the selection criteria rowsDeleted = contentResolver.delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to )
Java
// Defines selection criteria for the rows you want to delete String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] selectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int rowsDeleted = 0; ... // Deletes the words that match the selection criteria rowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // The UserDictionary content URI selectionClause, // The column to select on selectionArgs // The value to compare to );
Dọn dẹp hoạt động đầu vào của người dùng khi bạn gọi
ContentResolver.delete()
. Để tìm hiểu thêm về
hãy đọc phần Bảo vệ khỏi dữ liệu nhập độc hại.
Loại dữ liệu nhà cung cấp
Trình cung cấp nội dung có thể cung cấp nhiều loại dữ liệu. Nhà cung cấp từ điển người dùng chỉ cung cấp nhưng nhà cung cấp cũng có thể cung cấp các định dạng sau:
- số nguyên
- số nguyên dài (dài)
- dấu phẩy động
- dấu phẩy động dài (gấp đôi)
Một loại dữ liệu khác mà nhà cung cấp thường sử dụng là đối tượng nhị phân lớn (BLOB) được triển khai dưới dạng
Mảng byte 64 KB. Bạn có thể xem các loại dữ liệu có sẵn bằng cách xem
Cursor
lớp "nhận" .
Loại dữ liệu cho mỗi cột của nhà cung cấp thường được liệt kê trong tài liệu tương ứng.
Các loại dữ liệu cho Nhà cung cấp từ điển người dùng được liệt kê trong tài liệu tham khảo
cho lớp hợp đồng UserDictionary.Words
. Các lớp hợp đồng là
được mô tả trong phần Các lớp hợp đồng.
Bạn cũng có thể xác định loại dữ liệu bằng cách gọi Cursor.getType()
.
Nhà cung cấp cũng duy trì thông tin loại dữ liệu MIME cho từng URI nội dung mà họ xác định. Bạn có thể sử dụng thông tin loại MIME để tìm hiểu xem ứng dụng của bạn có thể xử lý dữ liệu mà hoặc để chọn một loại xử lý dựa trên loại MIME. Bạn thường cần Loại MIME khi bạn làm việc với một nhà cung cấp có chứa mã tệp hoặc cấu trúc dữ liệu.
Ví dụ: ContactsContract.Data
trong Trình cung cấp danh bạ sử dụng loại MIME để gắn nhãn loại dữ liệu liên hệ được lưu trữ trong mỗi
hàng. Để lấy loại MIME tương ứng với URI nội dung, hãy gọi
ContentResolver.getType()
.
Phần tài liệu tham khảo về loại MIME mô tả của cả hai loại MIME chuẩn và tuỳ chỉnh.
Các hình thức truy cập khác của nhà cung cấp
Có 3 hình thức quyền truy cập khác của trình cung cấp rất quan trọng trong quá trình phát triển ứng dụng:
-
Truy cập theo lô: bạn có thể tạo một loạt lệnh gọi truy cập bằng các phương thức trong
lớp
ContentProviderOperation
, sau đó áp dụng chúng vớiContentResolver.applyBatch()
. -
Truy vấn không đồng bộ: thực hiện các truy vấn trong một chuỗi riêng. Bạn có thể
hãy sử dụng đối tượng
CursorLoader
. Các ví dụ trong Minh hoạ hướng dẫn về trình tải cách thực hiện việc này. - Truy cập dữ liệu bằng ý định: mặc dù bạn không thể gửi ý định trực tiếp đến ứng dụng nhà cung cấp, bạn có thể gửi một ý định đến ứng dụng của nhà cung cấp, tức là thường được trang bị tốt nhất để chỉnh sửa dữ liệu của nhà cung cấp.
Quyền truy cập theo lô và sửa đổi bằng ý định được mô tả trong các phần sau.
Truy cập theo đợt
Quyền truy cập theo lô vào một trình cung cấp rất hữu ích khi chèn một số lượng lớn hàng, để chèn hàng trong nhiều bảng trong cùng một lệnh gọi phương thức và nói chung để thực hiện một tập hợp hoạt động xuyên qua ranh giới của quy trình dưới dạng giao dịch, được gọi là hoạt động nguyên tử.
Để truy cập vào nhà cung cấp ở chế độ hàng loạt,
tạo một mảng các đối tượng ContentProviderOperation
, sau đó
gửi chúng đến một nhà cung cấp nội dung có
ContentResolver.applyBatch()
Bạn truyền
quyền hạn của trình cung cấp nội dung đối với phương thức này, thay vì một URI nội dung cụ thể.
Điều này cho phép từng đối tượng ContentProviderOperation
trong mảng hoạt động
so với một bảng khác. Lệnh gọi đến ContentResolver.applyBatch()
trả về một mảng kết quả.
Nội dung mô tả về lớp hợp đồng ContactsContract.RawContacts
bao gồm một đoạn mã minh hoạ tính năng chèn hàng loạt.
Truy cập dữ liệu bằng ý định
Ý định có thể cung cấp quyền truy cập gián tiếp vào trình cung cấp nội dung. Bạn có thể cho phép người dùng truy cập trong một nhà cung cấp ngay cả khi ứng dụng của bạn không có quyền truy cập nhận lại ý đị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 có quyền và cho phép người dùng làm việc trong ứng dụng đó.
Nhận quyền truy cập bằng các quyền tạm thời
Bạn có thể truy cập dữ liệu trong trình cung cấp nội dung, ngay cả khi không có quyền truy cập thích hợp bằng cách gửi ý định đến một ứng dụng có quyền và nhận lại ý định kết quả có chứa quyền URI. Đây là các quyền cho một URI nội dung cụ thể kéo dài cho đến khi hoạt động nhận được chúng đã hoàn tất. Ứng dụng có quyền vĩnh viễn sẽ cấp quyền tạm thời bằng cách gắn cờ trong ý định kết quả:
-
Quyền đọc:
FLAG_GRANT_READ_URI_PERMISSION
-
Quyền ghi:
FLAG_GRANT_WRITE_URI_PERMISSION
Lưu ý: Những cờ này không cấp quyền đọc hoặc ghi chung cho trình cung cấp có quyền hạn trong URI nội dung. Quyền truy cập chỉ dành cho chính URI đó.
Khi bạn gửi URI nội dung đến một ứng dụng khác, hãy thêm ít nhất một trong hai thuộc tính này cờ. Cờ cung cấp các khả năng sau cho bất kỳ ứng dụng nào nhận được một ý định và nhắm đến Android 11 (API cấp 30) trở lên:
- Đọc hoặc ghi vào dữ liệu mà URI nội dung biểu thị, tuỳ thuộc vào cờ có trong ý định.
- Nhận gói chế độ hiển thị vào ứng dụng chứa trình cung cấp nội dung khớp với Đơn vị quản lý URI. Ứng dụng gửi ý định và ứng dụng gửi ý định chứa trình cung cấp nội dung có thể là hai ứng dụng khác nhau.
Nhà cung cấp xác định quyền URI cho URI nội dung trong tệp kê khai, bằng cách sử dụng
android:grantUriPermissions
của trang web
<provider>
cũng như
<grant-uri-permission>
là phần tử con của
<provider>
. Cơ chế cấp quyền URI được giải thích chi tiết hơn trong
Hướng dẫn về Quyền trên Android.
Ví dụ: bạn có thể truy xuất dữ liệu về một người liên hệ trong Trình cung cấp danh bạ, ngay cả khi bạn không
có quyền READ_CONTACTS
. Bạn nên
điều này bằng một ứng dụng gửi lời chào điện tử cho một người liên hệ vào ngày sinh nhật của họ. Thay vì
yêu cầu READ_CONTACTS
, giúp bạn có quyền truy cập vào tất cả
danh bạ và tất cả thông tin của họ, cho phép người dùng kiểm soát
địa chỉ liên hệ mà ứng dụng của bạn sử dụng. Để thực hiện việc này, hãy sử dụng quy trình sau:
-
Trong ứng dụng của bạn, hãy gửi một ý định chứa thao tác
ACTION_PICK
và "địa chỉ liên hệ" Loại MIMECONTENT_ITEM_TYPE
bằng cách sử dụng phương thứcstartActivityForResult()
. - Vì ý định này khớp với bộ lọc ý định cho "Lựa chọn" của ứng dụng người dùng hoạt động, hoạt động đó sẽ xuất hiện trên nền trước.
-
Trong hoạt động lựa chọn, người dùng chọn một
địa chỉ liên hệ để cập nhật. Khi điều này xảy ra, hoạt động lựa chọn sẽ gọi
setResult(resultcode, intent)
để thiết lập ý định trả lại ứng dụng của bạn. Ý định chứa URI nội dung của thông tin liên hệ mà người dùng đã chọn và các "thông tin bổ sung" cờFLAG_GRANT_READ_URI_PERMISSION
. Các cờ này cấp URI quyền truy cập vào ứng dụng của bạn để đọc dữ liệu cho địa chỉ liên hệ mà URI nội dung. Sau đó, hoạt động lựa chọn sẽ gọifinish()
tới trả về quyền kiểm soát cho ứng dụng của bạn. -
Hoạt động của bạn quay lại nền trước và hệ thống sẽ gọi
onActivityResult()
. Phương thức này nhận ý định kết quả do hoạt động lựa chọn tạo ra trong ứng dụng Liên hệ. - Với URI nội dung trong ý định kết quả, bạn có thể đọc dữ liệu của người liên hệ 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 vĩnh viễn cho trình cung cấp trong tệp kê khai của bạn. Sau đó, bạn có thể nhận thông tin ngày sinh của người liên hệ đó hoặc địa chỉ email, sau đó gửi lời chào điện tử.
Sử dụng ứng dụng khác
Một cách khác để 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à kích hoạt một ứng dụng có quyền và cho phép người dùng thực hiện công việc trong ứng dụng đó.
Ví dụ: ứng dụng Lịch chấp nhận một
Ý định ACTION_INSERT
cho phép bạn kích hoạt
giao diện người dùng chèn của ứng dụng. Bạn có thể chuyển "thông tin bổ sung" dữ liệu trong ý định này. Ứng dụng sẽ
sử dụng để điền sẵn vào giao diện người dùng. Vì sự kiện định kỳ có cú pháp phức tạp, nên phương thức
cách chèn sự kiện vào Nhà cung cấp lịch là kích hoạt ứng dụng Lịch bằng
ACTION_INSERT
, sau đó cho phép người dùng chèn sự kiện vào đó.
Hiển thị dữ liệu bằng ứng dụng trợ giúp
Nếu ứng dụng của bạn có quyền truy cập, bạn vẫn có thể sử dụng
ý đị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
Ý định ACTION_VIEW
hiển thị một ngày hoặc sự kiện cụ thể.
Điều này cho phép bạn hiển thị thông tin lịch mà không phải tạo giao diện người dùng của riêng mình.
Để tìm hiểu thêm về tính năng này, hãy xem
Tổng quan về nhà cung cấp lịch.
Ứng dụng mà bạn gửi ý định không nhất thiết phải là ứng dụng
liên kết với nhà cung cấp. Ví dụ: bạn có thể truy xuất người liên hệ từ
Liên hệ với Nhà cung cấp, sau đó gửi ý định ACTION_VIEW
chứa URI nội dung dành cho hình ảnh của người liên hệ với trình xem hình ảnh.
Lớp học theo hợp đồng
Lớp hợp đồng xác định các hằng số giúp ứng dụng làm việc với URI nội dung, cột
tên, thao tác theo ý định và các tính năng khác của trình cung cấp nội dung. Các lớp hợp đồng không có
tự động đi kèm với một nhà cung cấp. Nhà phát triển của nhà cung cấp phải xác định chúng, sau đó
cung cấp chúng cho các nhà phát triển khác. Nhiều nhà cung cấp đi kèm với Android
nền tảng có các lớp hợp đồng tương ứng trong gói android.provider
.
Ví dụ: Nhà cung cấp từ điển người dùng có một lớp hợp đồng
UserDictionary
chứa URI nội dung và hằng số tên cột. Chiến lược phát hành đĩa đơn
URI nội dung cho bảng Words
được định nghĩa trong hằng số
UserDictionary.Words.CONTENT_URI
Lớp UserDictionary.Words
cũng chứa hằng số tên cột,
được dùng trong đoạn mã ví dụ trong hướng dẫn này. Ví dụ: phép chiếu truy vấn có thể là
được xác định như sau:
Kotlin
val projection : Array<String> = arrayOf( UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE )
Java
String[] projection = { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE };
Một lớp hợp đồng khác là ContactsContract
cho Trình cung cấp danh bạ.
Tài liệu tham khảo cho lớp này bao gồm các đoạn mã mẫu. Một trong những
lớp con, ContactsContract.Intents.Insert
, là một hợp đồng
lớp chứa hằng số cho ý định và dữ liệu ý định.
Tài liệu tham khảo về loại MIME
Nhà cung cấp nội dung có thể trả về các loại nội dung nghe nhìn MIME chuẩn, chuỗi loại MIME tuỳ chỉnh hoặc cả hai.
Loại MIME có định dạng sau:
type/subtype
Ví dụ: loại MIME phổ biến text/html
có loại text
và
loại phụ html
. Nếu trình cung cấp trả về loại này cho một URI, thì có nghĩa là một
bằng cách sử dụng URI đó.
Chuỗi loại MIME tuỳ chỉnh, còn được gọi là loại MIME dành riêng cho nhà cung cấp, có nhiều kiểu MIME hơn các giá trị type và subtype phức. Đối với nhiều hàng, giá trị loại luôn là:
vnd.android.cursor.dir
Đối với một hàng, giá trị loại luôn là:
vnd.android.cursor.item
subtype là dành riêng cho từng nhà cung cấp. Các nhà cung cấp tích hợp sẵn cho Android thường có loại phụ. Ví dụ: khi ứng dụng Danh bạ tạo một hàng cho số điện thoại, mã này sẽ đặt loại MIME sau đây trong hàng:
vnd.android.cursor.item/phone_v2
Giá trị loại phụ là phone_v2
.
Các nhà phát triển nhà cung cấp khác có thể tạo mẫu phụ của riêng họ dựa trên
tên tổ chức và tên bảng. Ví dụ: hãy cân nhắc một nhà cung cấp có chứa lịch trình tàu hoả.
Thẩm quyền của trình cung cấp là com.example.trains
và có chứa các bảng
Dòng 1, Dòng 2 và Dòng 3. Để phản hồi URI nội dung sau đây cho bảng Line1:
content://com.example.trains/Line1
trình cung cấp sẽ trả về loại MIME sau đây:
vnd.android.cursor.dir/vnd.example.line1
Để phản hồi URI nội dung sau đây cho hàng 5 trong bảng Line2:
content://com.example.trains/Line2/5
trình cung cấp sẽ trả về loại MIME sau đây:
vnd.android.cursor.item/vnd.example.line2
Hầu hết trình cung cấp nội dung đều xác định hằng số lớp hợp đồng cho loại MIME mà họ sử dụng. Chiến lược phát hành đĩa đơn
Lớp hợp đồng Trình cung cấp danh bạ ContactsContract.RawContacts
,
ví dụ: xác định hằng số
CONTENT_ITEM_TYPE
cho loại MIME của
một hàng thông tin liên hệ thô.
URI nội dung cho các hàng đơn được mô tả trong phần phần URI nội dung.