page.title=Tạo một Trình cung cấp Nội dung @jd:body

Trong tài liệu này

  1. Thiết kế Kho lưu trữ Dữ liệu
  2. Thiết kế URI Nội dung
  3. Triển khai Lớp Trình cung cấp Nội dung
    1. Phương pháp được Yêu cầu
    2. Triển khai phương pháp query()
    3. Triển khai phương pháp insert()
    4. Triển khai phương pháp delete()
    5. Triển khai phương pháp update()
    6. Triển khai phương pháp onCreate()
  4. Triển khai Kiểu MIME của Trình cung cấp Nội dung
    1. Kiểu MIME cho bảng
    2. Kiểu MIME cho tệp
  5. Triển khai một Lớp Hợp đồng
  6. Triển khai Quyền của Trình cung cấp Nội dung
  7. Phần tử <provider>
  8. Ý định và Truy cập Dữ liệu

Lớp khóa

  1. {@link android.content.ContentProvider}
  2. {@link android.database.Cursor}
  3. {@link android.net.Uri}

Các Mẫu Liên quan

  1. Ứng dụng mẫu Note Pad

Xem thêm

  1. Nội dung Cơ bản về Trình cung cấp Nội dung
  2. Trình cung cấp Lịch

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. Bạn triển khai một trình cung cấp thành một hoặc nhiều lớp trong một ứng dụng Android, bên cạnh các phần tử trong tệp bản kê khai. Một trong các lớp của bạn triển khai một lớp con {@link android.content.ContentProvider}, đây là giao diện giữa trình cung cấp của bạn và các ứng dụng khác. Mặc dù mục đích của các trình cung cấp nội dung khác là cung cấp dữ liệu có sẵn cho các ứng dụng khác, dĩ nhiên bạn có thể ra lệnh cho các hoạt động trong ứng dụng của mình truy vấn và sửa đổi dữ liệu được quản lý bởi trình cung cấp của bạn.

Phần còn lại của chủ đề này là một danh sách cơ bản về các bước để xây dựng một trình cung cấp nội dung và một danh sách các API để sử dụng.

Trước khi Bạn Bắt đầu Xây dựng

Trước khi bạn bắt đầu xây dựng một trình cung cấp, hãy làm việc sau:

  1. Quyết định xem bạn có cần một trình cung cấp nội dung không. Bạn cần xây dựng một trình cung cấp nội dung nếu muốn cung cấp một hoặc nhiều tính năng sau đây:

    Bạn không cần trình cung cấp phải sử dụng một cơ sở dữ liệu SQLite nếu việc sử dụng hoàn toàn diễn ra trong ứng dụng của bạn.

  2. Nếu bạn chưa làm như vậy, hãy đọc chủ đề Nội dung Cơ bản về Trình cung cấp Nội dung để tìm hiểu thêm về trình cung cấp.

Tiếp theo, hãy làm theo những bước sau để xây dựng trình cung cấp của bạn:

  1. Thiết kế kho lưu trữ thô cho dữ liệu của bạn. Một trình cung cấp nội dung sẽ cung cấp dữ liệu theo hai cách:
    Dữ liệu tệp
    Dữ liệu mà thường đến các tệp chẳng hạn như ảnh, âm thanh, hoặc video. Lưu trữ các tệp ở không gian riêng tư trong ứng dụng của bạn. Để hồi đáp lại một yêu cầu tệp từ một ứng dụng khác, trình cung cấp của bạn có thể cung cấp một núm điều tác cho tệp.
    Dữ liệu "cấu trúc"
    Dữ liệu mà thường đến một cơ sở dữ liệu, mảng, hoặc cấu trúc tương tự. Lưu trữ dữ liệu dưới dạng tương thích với các bảng hàng cột. Hàng biểu diễn một đối tượng, chẳng hạn như một người hoặc khoản mục trong kiểm kê. Cột biểu diễn một số dữ liệu cho đối tượng, chẳng hạn như tên của một người hoặc giá của một khoản mục. Một cách thường dùng để lưu trữ loại dữ liệu này đó là trong cơ sở dữ liệu SQLite, nhưng bạn có thể sử dụng bất kỳ loại kho lưu trữ lâu dài nào. Để tìm hiểu thêm về các loại kho lưu trữ có sẵn trong hệ thống Android, hãy xem phần Thiết kế Kho lưu trữ Dữ liệu.
  2. Định nghĩa một triển khai cụ thể của lớp {@link android.content.ContentProvider} và các phương pháp được yêu cầu của nó. Lớp này là giao diện giữa dữ liệu của bạn và phần còn lại của hệ thống Android. Để biết thêm thông tin về lớp này, hãy xem phần Triển khai Lớp ContentProvider.
  3. Định nghĩa xâu thẩm quyền của trình cung cấp, URI nội dung của nó, và các tên cột. Nếu bạn muốn ứng dụng của trình cung cấp xử lý các ý định, hãy định nghĩa các hành động ý định, dữ liệu phụ thêm, và cờ. Đồng thời, hãy định nghĩa các quyền mà bạn sẽ yêu cầu cho những ứng dụng muốn truy cập dữ liệu của bạn. Bạn nên cân nhắc định nghĩa tất cả những giá trị này là hằng số trong một lớp riêng; sau đó, bạn có thể cho hiện lớp này ra với các nhà phát triển khác. Để biết thêm thông tin về URI nội dung, hãy xem phần Thiết kế URI Nội dung. Để biết thêm thông tin về ý định, hãy xem phần Ý định và Truy cập Dữ liệu.
  4. Thêm các nội dung tùy chọn khác, chẳng hạn như dữ liệu mẫu hoặc triển khai {@link android.content.AbstractThreadedSyncAdapter} mà có thể đồng bộ hoá dữ liệu giữa trình cung cấp và dữ liệu nền đám mây.

Thiết kế Kho lưu trữ Dữ liệu

Trình cung cấp nội dung là giao diện đối với dữ liệu được lưu theo một định dạng cấu trúc. Trước khi tạo giao diện, bạn phải quyết định cách lưu trữ dữ liệu. Bạn có thể lưu trữ dữ liệu theo bất kỳ dạng nào mà bạn muốn rồi thiết kế giao diện để đọc và ghi dữ liệu nếu cần thiết.

Có một số công nghệ lưu trữ dữ liệu có sẵn trong Android:

Những nội dung cần xem xét khi thiết kế dữ liệu

Sau đây là một số mẹo để thiết kế cấu trúc dữ liệu cho trình cung cấp của bạn:

Thiết kế 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 mang tính biểu tượng của toàn bộ trình cung cấp (quyền của nó) và một tên trỏ đến một bảng hoặc tệp (đường dẫn). Phần id tùy chọn chỉ đến một hàng riêng lẻ trong một bảng. Mọi phương thức truy cập dữ liệu {@link android.content.ContentProvider} đều có một URI nội dung là một tham đối; điều này cho phép bạn xác định bảng, hàng, hoặc tệp để truy cập.

Nội dung cơ bản của URI nội dung được mô tả trong chủ đề Nội dung Cơ bản về Trình cung cấp Nội dung.

Thiết kế một thẩm quyền

Một trình cung cấp thường có một thẩm quyền duy nhất, đóng vai trò là tên nội bộ Android của nó. Để tránh xung đột với các trình cung cấp khác, bạn nên sử dụng quyền sở hữu miền Internet (đảo ngược) làm cơ sở cho thẩm quyền của trình cung cấp của mình. Vì đề xuất này cũng đúng đối với tên gói Android, bạn có thể định nghĩa thẩm quyền trình cung cấp của mình là phần mở rộng của tên gói chứa trình cung cấp. Ví dụ, nếu tên gói Android là com.example.<appname>, bạn nên cấp cho trình cung cấp của mình thẩm quyền com.example.<appname>.provider.

Thiết kế một cấu trúc đường dẫn

Nhà phát triển thường tạo URI nội dung từ thẩm quyền bằng cách nối các đường dẫn trỏ đến các bảng riêng lẻ. Ví dụ, nếu bạn có hai bảng table1table2, bạn kết hợp thẩm quyền từ ví dụ trước để tạo ra các URI nội dung com.example.<appname>.provider/table1com.example.<appname>.provider/table2. Các đường dẫn không bị giới hạn ở một phân đoạn duy nhất, và không cần phải có một bảng cho từng cấp của đường dẫn.

Xử lý ID URI nội dung

Theo quy ước, các trình cung cấp cho phép truy cập một hàng đơn trong một bảng bằng cách chấp nhận một URI nội dung có một giá trị ID cho hàng đó ở cuối URI. Cũng theo quy ước, các trình cung cấp sẽ so khớp giá trị ID với cột _ID của bảng, và thực hiện truy cập yêu cầu đối với hàng trùng khớp.

Quy ước này tạo điều kiện cho một kiểu mẫu thiết kế chung cho các ứng dụng truy cập một trình cung cấp. Ứng dụng tiến hành truy vấn đối với trình cung cấp và hiển thị kết quả {@link android.database.Cursor} trong một {@link android.widget.ListView} bằng cách sử dụng {@link android.widget.CursorAdapter}. Định nghĩa {@link android.widget.CursorAdapter} yêu cầu một trong các cột trong {@link android.database.Cursor} phải là _ID

Sau đó, người dùng chọn một trong các hàng được hiển thị từ UI để xem hoặc sửa đổi dữ liệu. Ứng dụng sẽ nhận được hàng tương ứng từ {@link android.database.Cursor} làm nền cho {@link android.widget.ListView}, nhận giá trị _ID cho hàng này, nối nó với URI nội dung, và gửi yêu cầu truy cập tới trình cung cấp. Sau đó, trình cung cấp có thể thực hiện truy vấn hoặc sửa đổi đối với chính xác hàng mà người dùng đã chọn.

Kiểu mẫu URI nội dung

Để giúp bạn chọn hành động nào sẽ thực hiện cho URI nội dung đến, API của trình cung cấp sẽ bao gồm lớp thuận tiện {@link android.content.UriMatcher}, nó ánh xạ "kiểu mẫu" URI nội dung với các giá trị số nguyên. Bạn có thể sử dụng các giá trị số nguyên trong một câu lệnh switch mà chọn hành động mong muốn cho URI nội dung hoặc URI mà khớp với một kiểu mẫu cụ thể.

Kiểu mẫu URI nội dung sẽ so khớp các URI nội dung bằng cách sử dụng ký tự đại diện:

Lấy một ví dụ về thiết kế và tạo mã xử lý URI nội dung, hãy xét một trình cung cấp có thẩm quyền com.example.app.provider mà nhận ra các URI nội dung trỏ đến các bảng sau:

Trình cung cấp cũng nhận ra những URI nội dung này nếu chúng có một ID hàng được nối kèm, như ví dụ content://com.example.app.provider/table3/1 đối với hàng được nhận biết bởi 1 trong table3.

Sẽ có thể có các kiểu mẫu URI nội dung sau:

content://com.example.app.provider/*
Khớp với bất kỳ URI nội dung nào trong trình cung cấp.
content://com.example.app.provider/table2/*:
Khớp với một URI nội dung cho các bảng dataset1dataset2, nhưng không khớp với URI nội dung cho table1 hoặc table3.
content://com.example.app.provider/table3/#: Khớp với một URI nội dung cho các hàng đơn trong table3, chẳng hạn như content://com.example.app.provider/table3/6 đối với hàng được xác định bởi 6.

Đoạn mã HTML sau cho biết cách hoạt động của các phương pháp trong {@link android.content.UriMatcher}. Đoạn mã này xử lý các URI cho toàn bộ một bảng khác với URI cho một hàng đơn, bằng cách sử dụng mẫu hình URI nội dung content://<authority>/<path> cho các bảng, và content://<authority>/<path>/<id> cho các hàng đơn.

Phương pháp {@link android.content.UriMatcher#addURI(String, String, int) addURI()} ánh xạ một thẩm quyền và đường dẫn tới một giá trị số nguyên. Phương pháp {@link android.content.UriMatcher#match(Uri) match()} trả về giá trị số nguyên cho một URI. Câu lệnh switch sẽ chọn giữa truy vấn toàn bộ bảng và truy vấn cho một bản ghi đơn:

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher sUriMatcher;
...
    /*
     * The calls to addURI() go here, for all of the content URI patterns that the provider
     * should recognize. For this snippet, only the calls for table 3 are shown.
     */
...
    /*
     * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
     * in the path
     */
    sUriMatcher.addURI("com.example.app.provider", "table3", 1);

    /*
     * Sets the code for a single row to 2. In this case, the "#" wildcard is
     * used. "content://com.example.app.provider/table3/3" matches, but
     * "content://com.example.app.provider/table3 doesn't.
     */
    sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (sUriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query
                 */
                selection = selection + "_ID = " uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI is not recognized, you should do some error handling here.
        }
        // call the code to actually do the query
    }

Một lớp khác, {@link android.content.ContentUris}, sẽ cung cấp các phương pháp thuận tiện để làm việc với phần id của URI nội dung. Các lớp {@link android.net.Uri} và {@link android.net.Uri.Builder} bao gồm các phương pháp thuận tiện cho việc phân tích các đối tượng {@link android.net.Uri} hiện có và xây dựng các đối tượng mới.

Triển khai Lớp Trình cung cấp Nội dung

Thực thể {@link android.content.ContentProvider} quản lý truy cập vào một tập dữ liệu cấu trúc bằng cách xử lý yêu cầu từ các ứng dụng khác. Tất cả các dạng truy cập cuối cùng đều gọi {@link android.content.ContentResolver}, sau đó nó gọi ra một phương pháp cụ thể của {@link android.content.ContentProvider} để lấy quyền truy cập.

Phương pháp được yêu cầu

Lớp tóm tắt {@link android.content.ContentProvider} sẽ định nghĩa sáu phương pháp tóm tắt mà bạn phải triển khai như một phần lớp con cụ thể của mình. Tất cả những phương pháp này ngoại trừ {@link android.content.ContentProvider#onCreate() onCreate()} đều được gọi ra bởi một ứng dụng máy khách đang cố truy cập trình cung cấp nội dung của bạn:

{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) query()}
Truy xuất dữ liệu từ trình cung cấp của bạn. Sử dụng các tham đối để chọn bảng để truy vấn, các hàng và cột để trả về, và thứ tự sắp xếp của kết quả. Trả về dữ liệu như một đối tượng {@link android.database.Cursor}.
{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}
Chèn một hàng mới vào trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn bảng đích và nhận các giá trị cột để sử dụng. Trả về một URI nội dung cho hàng mới chèn.
{@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()}
Cập nhật các hàng hiện tại trong trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn bảng và hàng để cập nhật và nhận các giá trị cột được cập nhật. Trả về số hàng được cập nhật.
{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()}
Xóa hàng khỏi trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn bảng và các hàng cần xóa. Trả về số hàng được xóa.
{@link android.content.ContentProvider#getType(Uri) getType()}
Trả về kiểu MIME tương ứng với một URI nội dung. Phương pháp này được mô tả chi tiết hơn trong phần Triển khai Kiểu MIME của Trình cung cấp Nội dung.
{@link android.content.ContentProvider#onCreate() onCreate()}
Khởi tạo trình cung cấp của bạn. Hệ thống Android sẽ gọi ra phương pháp này ngay lập tức sau khi nó tạo trình cung cấp của bạn. Để ý rằng trình cung cấp của bạn không được tạo cho đến khi đối tượng {@link android.content.ContentResolver} cố truy cập nó.

Để ý rằng những phương pháp này có cùng chữ ký như các phương pháp {@link android.content.ContentResolver} được đặt tên như nhau.

Việc bạn triển khai những phương pháp này nên xét tới các nội dung sau:

Triển khai phương pháp query()

Phương pháp {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()} phải trả về một đối tượng {@link android.database.Cursor}, nếu không nó sẽ thất bại , đưa ra một lỗi {@link java.lang.Exception}. Nếu bạn đang sử dụng một cơ sở dữ liệu SQLite làm kho lưu trữ dữ liệu của mình , bạn có thể chỉ cần trả về {@link android.database.Cursor} được trả về bởi một trong các phương pháp query() của lớp {@link android.database.sqlite.SQLiteDatabase}. Nếu truy vấn không khớp với bất kỳ hàng nào, bạn nên trả về một thực thể {@link android.database.Cursor} có phương pháp {@link android.database.Cursor#getCount()} trả về 0. Bạn chỉ nên trả về null nếu đã xảy ra một lỗi nội bộ trong tiến trình truy vấn.

Nếu bạn không đang sử dụng một cơ sở dữ liệu SQLite làm kho lưu trữ dữ liệu của mình, hãy sử dụng một trong các lớp con cụ thể của {@link android.database.Cursor}. Ví dụ, lớp {@link android.database.MatrixCursor} sẽ triển khai một con chạy trong đó mỗi hàng là một mảng của {@link java.lang.Object}. Với lớp này, hãy sử dụng {@link android.database.MatrixCursor#addRow(Object[]) addRow()} để thêm một hàng mới.

Nhớ rằng hệ thống Android phải có thể giao tiếp với {@link java.lang.Exception} qua các ranh giới tiến trình. Android có thể làm vậy cho những trường hợp ngoại lệ sau, điều này có thể hữu ích trong xử lý lỗi truy vấn:

Triển khai phương pháp insert()

Phương pháp {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} sẽ thêm một hàng mới vào bảng phù hợp bằng cách sử dụng các giá trị trong tham đối {@link android.content.ContentValues} . Nếu tên cột không nằm trong tham đối {@link android.content.ContentValues}, bạn có thể muốn cung cấp một giá trị mặc định cho nó hoặc trong mã trình cung cấp của bạn hoặc trong sơ đồ cơ sở dữ liệu của bạn.

Phương pháp này sẽ trả về URI nội dung cho hàng mới. Để xây dựng điều này, hãy nối giá trị _ID của hàng mới (hay khóa chính khác) với URI nội dung của bảng bằng cách sử dụng {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}.

Triển khai phương pháp delete()

Phương pháp {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} không cần phải xóa hàng thực chất khỏi kho lưu trữ dữ liệu của bạn. Nếu bạn đang sử dụng một trình điều hợp đồng bộ với trình cung cấp của mình, bạn nên cân nhắc đánh dấu một hàng đã xóa bằng cờ "xóa" thay vì gỡ bỏ hàng một cách hoàn toàn. Trình điều hợp đồng bộ có thể kiểm tra các hàng đã xóa và gỡ bỏ chúng khỏi máy chủ trước khi xóa chúng khỏi trình cung cấp.

Triển khai phương pháp update()

Phương pháp {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()} lấy cùng tham đối {@link android.content.ContentValues} được sử dụng bởi {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}, và cùng tham đối selectionselectionArgs được sử dụng bởi {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} và {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()}. Điều này có thể cho phép bạn sử dụng lại mã giữa những phương pháp này.

Triển khai phương pháp onCreate()

Hệ thống Android sẽ gọi {@link android.content.ContentProvider#onCreate() onCreate()} khi nó khởi động trình cung cấp. Bạn chỉ nên thực hiện các tác vụ khởi tạo chạy nhanh trong phương pháp này, và hoãn việc tạo cơ sở dữ liệu và nạp dữ liệu tới khi trình cung cấp thực sự nhận được yêu cầu cho dữ liệu. Nếu bạn thực hiện các tác vụ dài trong {@link android.content.ContentProvider#onCreate() onCreate()}, bạn sẽ làm chậm lại quá trình khởi động của trình cung cấp. Đến lượt mình, điều này sẽ làm chậm hồi đáp từ trình cung cấp đối với các ứng dụng khác.

Ví dụ, nếu bạn đang sử dụng một cơ sở dữ liệu SQLite, bạn có thể tạo một đối tượng {@link android.database.sqlite.SQLiteOpenHelper} mới trong {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}, rồi tạo các bảng SQL lần đầu tiên khi bạn mở cơ sở dữ liệu. Để tạo điều kiện cho điều này, lần đầu tiên bạn gọi {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase getWritableDatabase()}, nó sẽ tự động gọi ra phương pháp {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()}.

Hai đoạn mã HTML sau minh họa tương tác giữa {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} và {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()}. Đoạn mã HTML đầu tiên là triển khai {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}:

public class ExampleProvider extends ContentProvider

    /*
     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
     * in a following snippet.
     */
    private MainDatabaseHelper mOpenHelper;

    // Defines the database name
    private static final String DBNAME = "mydb";

    // Holds the database object
    private SQLiteDatabase db;

    public boolean onCreate() {

        /*
         * Creates a new helper object. This method always returns quickly.
         * Notice that the database itself isn't created or opened
         * until SQLiteOpenHelper.getWritableDatabase is called
         */
        mOpenHelper = new MainDatabaseHelper(
            getContext(),        // the application context
            DBNAME,              // the name of the database)
            null,                // uses the default SQLite cursor
            1                    // the version number
        );

        return true;
    }

    ...

    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which table to open, handle error-checking, and so forth

        ...

        /*
         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
         *
         */
        db = mOpenHelper.getWritableDatabase();
    }
}

Đoạn mã HTML tiếp theo là triển khai {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()}, bao gồm một lớp trình trợ giúp:

...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    "main " +                       // Table's name
    "(" +                           // The columns in the table
    " _ID INTEGER PRIMARY KEY, " +
    " WORD TEXT"
    " FREQUENCY INTEGER " +
    " LOCALE TEXT )";
...
/**
 * Helper class that actually creates and manages the provider's underlying data repository.
 */
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    /*
     * Instantiates an open helper for the provider's SQLite data repository
     * Do not do database creation and upgrade here.
     */
    MainDatabaseHelper(Context context) {
        super(context, DBNAME, null, 1);
    }

    /*
     * Creates the data repository. This is called when the provider attempts to open the
     * repository and SQLite reports that it doesn't exist.
     */
    public void onCreate(SQLiteDatabase db) {

        // Creates the main table
        db.execSQL(SQL_CREATE_MAIN);
    }
}

Triển khai Kiểu MIME của Trình cung cấp Nội dung

Lớp {@link android.content.ContentProvider} có hai phương pháp để trả về các kiểu MIME:

{@link android.content.ContentProvider#getType(Uri) getType()}
Một trong các phương pháp được yêu cầu mà bạn phải triển khai cho bất kỳ trình cung cấp nào.
{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}
Một phương pháp mà bạn được dự tính sẽ triển khai nếu trình cung cấp của bạn cung cấp tệp.

Kiểu MIME cho bảng

Phương pháp {@link android.content.ContentProvider#getType(Uri) getType()} trả về một {@link java.lang.String} theo định dạng MIME mà mô tả kiểu dữ liệu được trả về bởi tham đối URI nội dung. Tham đối {@link android.net.Uri} có thể là một mẫu hình thay vì một URI cụ thể; trong trường hợp này, bạn nên trả về kiểu dữ liệu được liên kết với các URI nội dung mà khớp với mẫu hình đó.

Đối với các kiểu dữ liệu phổ biến như văn bản, HTML, hay JPEG, {@link android.content.ContentProvider#getType(Uri) getType()} sẽ trả về kiểu MIME tiêu chuẩn cho dữ liệu đó. Một danh sách đầy đủ về những kiểu tiêu chuẩn này có sẵn trên trang web IANA MIME Media Types .

Đối với các URI nội dung mà trỏ tới một hàng hoặc các hàng của bảng dữ liệu, {@link android.content.ContentProvider#getType(Uri) getType()} sẽ trả về một kiểu MIME theo định dạng MIME riêng cho nhà cung cấp của Android:

Ví dụ, nếu thẩm quyền của một trình cung cấp là com.example.app.provider, và nó làm hiện ra một bảng có tên table1 thì kiểu MIME cho nhiều hàng trong table1 là:

vnd.android.cursor.dir/vnd.com.example.provider.table1

Đối với một hàng đơn của table1, kiểu MIME là:

vnd.android.cursor.item/vnd.com.example.provider.table1

Kiểu MIME cho tệp

Nếu trình cung cấp của bạn cung cấp tệp, hãy triển khai {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}. Phương pháp này sẽ trả về một mảng {@link java.lang.String} của kiểu MIME đối với các tệp mà trình cung cấp của bạn có thể trả về cho một URI nội dung cho trước. Bạn nên lọc các kiểu MIME mà mình cung cấp bằng tham đối bộ lọc kiểu MIME, sao cho bạn chỉ trả về những kiểu MIME mà máy khách muốn xử lý.

Ví dụ, xét một trình cung cấp hình ảnh dưới dạng tệp có định dạng .jpg, .png.gif. Nếu một ứng dụng gọi {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} bằng xâu bộ lọc image/* ( mà là một "hình ảnh"), khi đó phương pháp {@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} sẽ trả về mảng:

{ "image/jpeg", "image/png", "image/gif"}

Nếu ứng dụng chỉ quan tâm đến các tệp .jpg, vậy nó có thể gọi {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} bằng xâu bộ lọc *\/jpeg, và {@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} sẽ trả về:

{"image/jpeg"}

Nếu trình cung cấp của bạn không cung cấp bất kỳ kiểu MIME nào được yêu cầu trong xâu bộ lọc, {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} sẽ trả về null.

Triển khai một Lớp Hợp đồng

Lớp hợp đồng là một lớp public final chứa các định nghĩa hằng số cho URI, tên cột, kiểu MIME, và siêu dữ liệu khác liên quan tới trình cung cấp. Lớp này sẽ thiết lập một hợp đồng giữa trình cung cấp và các ứng dụng khác bằng cách đảm bảo rằng trình cung cấp có thể được truy cập đúng ngay cả khi có thay đổi về giá trị thực sự của URI, tên cột, v.v.

Lớp hợp đồng cũng giúp các nhà phát triển vì chúng thường có tên dễ nhớ cho các hằng số của mình, vì vậy các nhà phát triển ít có khả năng sử dụng các giá trị không đúng cho tên cột hay URI hơn. Do đó là một lớp, nó có thể chứa tài liệu Javadoc. Các môi trường phát triển tích hợp như Eclipse có thể tự động điền các tên hằng số từ lớp hợp đồng và hiển thị Javadoc cho các hằng số đó.

Các nhà phát triển không thể truy cập tệp lớp của lớp hợp đồng từ ứng dụng của mình, nhưng họ có thể lặng lẽ biên dịch nó vào ứng dụng của họ từ một tệp .jar mà bạn cung cấp.

Lớp {@link android.provider.ContactsContract} và các lớp lồng nhau của nó là các ví dụ về lớp hợp đồng.

Triển khai Quyền của Trình cung cấp Nội dung

Quyền và truy cập đối với tất cả khía cạnh trong hệ thống Android được mô tả chi tiết trong chủ đề Bảo mật và Quyền. Chủ đề Kho lưu trữ Dữ liệu cũng mô tả bảo mật và các quyền có hiệu lực cho nhiều loại kho lưu trữ khác nhau. Nói tóm lại, các điểm quan trọng là:

Nếu bạn muốn sử dụng các quyền của trình cung cấp nội dung để kiểm soát truy cập vào dữ liệu của mình, khi đó bạn nên lưu trữ dữ liệu của mình trong các tệp nội bộ, cơ sở dữ liệu SQLite, hoặc "đám mây" (ví dụ, trên một máy chủ từ xa), và bạn nên giữ các tệp và cơ sở dữ liệu riêng tư cho ứng dụng của mình.

Triển khai quyền

Tất cả ứng dụng đều có thể đọc từ hoặc ghi vào trình cung cấp của bạn, ngay cả khi dữ liệu liên quan là dữ liệu riêng tư, vì theo mặc định, trình cung cấp của bạn không được đặt quyền. Để thay đổi điều này, hãy đặt quyền cho trình cung cấp của bạn trong tệp bản kê khai của bạn bằng cách sử dụng các thuộc tính hoặc phần tử con của phần tử <provider>. Bạn có thể đặt quyền áp dụng cho toàn bộ trình cung cấp, hoặc cho một số bảng, hoặc thậm chí cho một số bản ghi, hoặc cả ba.

Bạn định nghĩa các quyền cho trình cung cấp của bạn bằng một hoặc nhiều phần tử <permission> trong tệp bản kê khai của bạn. Để quyền là duy nhất cho trình cung cấp của bạn, hãy sử dụng phạm vi kiểu Java cho thuộc tính android:name. Ví dụ, đặt tên quyền đọc com.example.app.provider.permission.READ_PROVIDER.

Danh sách sau liệt kê phạm vi các quyền của trình cung cấp, bắt đầu với các quyền áp dụng cho toàn bộ trình cung cấp rồi mới đến các quyền chi tiết hơn. Các quyền chi tiết hơn được ưu tiên so với các quyền có phạm vi rộng hơn:

Quyền đọc-ghi đơn lẻ ở cấp trình cung cấp
Một quyền kiểm soát cả quyền truy cập đọc và ghi cho toàn bộ trình cung cấp, được quy định bằng thuộc tính android:permission của phần tử <provider>.
Quyền đọc ghi tách riêng ở cấp độ trình cung cấp
Một quyền đọc và một quyền ghi cho toàn bộ trình cung cấp. Bạn chỉ định chúng bằng các thuộc tính android:readPermission android:writePermission của phần tử <provider>. Chúng được ưu tiên so với quyền được yêu cầu bởi android:permission.
Quyền ở cấp đường dẫn
Quyền đọc, ghi, hoặc đọc/ghi cho một URI nội dung trong trình cung cấp của bạn. Bạn chỉ định từng URI mà bạn muốn kiểm soát bằng một phần tử con <path-permission> của phần tử <provider>. Với mỗi một URI nội dung mà bạn chỉ định, bạn có thể chỉ định một quyền đọc/ghi, quyền đọc, hoặc quyền ghi, hoặc cả ba. Quyền đọc và quyền ghi được ưu tiên so với quyền đọc/ghi. Đồng thời, quyền ở cấp độ đường dẫn sẽ được ưu tiên so với quyền ở cấp độ trình cung cấp.
Quyền tạm thời
Là cấp độ quyền cho phép truy cập tạm thời vào một ứng dụng, ngay cả khi ứng dụng không có các quyền thường được yêu cầu. Tính năng truy cập tạm thời làm giảm số quyền mà một ứng dụng phải yêu cầu trong bản kê khai của mình. Khi bạn dùng đến các quyền tạm thời, những ứng dụng duy nhất mà cần quyền "lâu dài" cho trình cung cấp của bạn là những ứng dụng liên tục truy cập tất cả dữ liệu của bạn.

Xét các quyền bạn cần để triển khai một trình cung cấp và ứng dụng e-mail khi bạn muốn cho phép một ứng dụng trình xem ảnh bên ngoài hiển thị các tài liệu đính kèm dạng ảnh từ trình cung cấp của bạn. Để cấp cho trình xem ảnh quyền truy cập cần thiết mà không cần yêu cầu quyền, hãy thiết lập các quyền tạm thời cho URI nội dung đối với ảnh. Thiết kế ứng dụng e-mail của bạn sao cho khi người dùng muốn hiển thị một ảnh, ứng dụng sẽ gửi một ý định chứa URI nội dung của ảnh và cờ cho phép tới trình xem ảnh. Trình xem ảnh khi đó có thể truy vấn trình cung cấp e-mail của bạn để truy xuất ảnh, ngay cả khi trình xem không có quyền đọc bình thường cho trình cung cấp của bạn.

Để sử dụng các quyền tạm thời, hoặc đặt thuộc tính android:grantUriPermissions của phần tử <provider> hoặc thêm một hoặc nhiều phần tử con <grant-uri-permission> vào phần tử <provider> của bạn. Nếu bạn sử dụng các quyền tạm thời, bạn phải gọi {@link android.content.Context#revokeUriPermission(Uri, int) Context.revokeUriPermission()} bất cứ khi nào bạn gỡ bỏ hỗ trợ cho một URI nội dung khỏi trình cung cấp của mình, và URI nội dung đó sẽ được liên kết với một quyền tạm thời.

Giá trị của thuộc tính sẽ xác định trình cung cấp của bạn được cho phép truy cập bao nhiêu. Nếu thuộc tính được đặt thành true, khi đó hệ thống sẽ cấp quyền tạm thời cho toàn bộ trình cung cấp của bạn, khống chế mọi quyền khác mà được yêu cầu bởi quyền ở cấp độ trình cung cấp hoặc cấp độ đường dẫn của bạn.

Nếu cờ này được đặt thành false, khi đó bạn phải thêm các phần tử con <grant-uri-permission> vào phần tử <provider> của mình. Mỗi phần tử con lại quy định URI nội dung hoặc các URI mà truy cập tạm thời được cấp cho.

Để ủy quyền truy cập tạm thời cho một ứng dụng, ý định phải chứa cờ {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} hoặc cờ {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, hoặc cả hai. Những quyền này được đặt bằng phương pháp {@link android.content.Intent#setFlags(int) setFlags()}.

Nếu thuộc tính android:grantUriPermissions không có mặt, giả sử rằng nó là false.

Phần tử <provider>

Như các thành phần {@link android.app.Activity} và {@link android.app.Service}, một lớp con của {@link android.content.ContentProvider} phải được định nghĩa trong tệp bản kê khai cho ứng dụng của nó bằng cách sử dụng phần tử <provider>. Hệ thống Android nhận thông tin sau từ phần tử:

Thẩm quyền ({@code android:authorities})
Các tên biểu tượng nhận biết toàn bộ trình cung cấp trong hệ thống. Thuộc tính này được mô tả chi tiết hơn trong phần Thiết kế URI Nội dung.
Tên lớp của trình cung cấp ( android:name )
Lớp triển khai {@link android.content.ContentProvider}. Lớp này được mô tả chi tiết hơn trong phần Triển khai Lớp Trình cung cấp Nội dung.
Quyền
Những thuộc tính quy định quyền mà các ứng dụng khác phải có để truy cập dữ liệu của trình cung cấp:

Các quyền và thuộc tính tương ứng của chúng được mô tả chi tiết hơn trong phần Triển khai Quyền của Trình cung cấp Nội dung.

Thuộc tính khởi động và kiểm soát
Những thuộc tính này xác định cách và thời điểm hệ thống Android khởi động trình cung cấp, các đặc tính tiến trình của trình cung cấp, và các thiết đặt về thời gian chạy:

Các thuộc tính được lập tài liệu theo dõi đầy đủ trong chủ đề hướng dẫn nhà phát triển đối với phần tử <provider> .

Các thuộc tính thông tin
Một biểu tượng tùy chọn và nhãn cho trình cung cấp:

Các thuộc tính được lập tài liệu theo dõi đầy đủ trong chủ đề hướng dẫn nhà phát triển đối với phần tử <provider>.

Ý định và Truy cập Dữ liệu

Các ứng dụng có thể gián tiếp truy cập một trình cung cấp nội dung bằng một {@link android.content.Intent}. Ứng dụng không gọi bất kỳ phương pháp nào của {@link android.content.ContentResolver} hoặc {@link android.content.ContentProvider}. Thay vào đó, nó sẽ gửi một ý định để bắt đầu một hoạt động, đây thường là một bộ phận trong ứng dụng của chính trình cung cấp. Hoạt động đích phụ trách truy xuất và hiển thị dữ liệu trong UI của nó. Tùy vào hành động trong ý định, hoạt động đích cũng có thể nhắc người dùng thực hiện sửa đổi dữ liệu của trình cung cấp. Một ý định cũng có thể chứa dữ liệu "phụ thêm" mà hoạt động đích hiển thị trong UI; khi đó người dùng có tùy chọn thay đổi dữ liệu này trước khi sử dụng nó để sửa đổi dữ liệu trong trình cung cấp.

Bạn có thể muốn sử dụng truy cập ý định để giúp đảm bảo toàn vẹn dữ liệu. Trình cung cấp của bạn có thể phụ thuộc vào việc chèn, cập nhật và xóa dữ liệu theo lô-gic nghiệp vụ được quy định chặt chẽ. Trong trường hợp như vậy, việc cho phép các ứng dụng khác trực tiếp sửa đổi dữ liệu của bạn có thể dẫn đến dữ liệu không hợp lệ. Nếu bạn muốn các nhà phát triển sử dụng truy cập ý định, hãy đảm bảo lập tài liệu theo dõi nó thật kỹ. Giải thích với họ tại sao truy cập ý định sử dụng UI ứng dụng của chính bạn lại tốt hơn là cố gắng sửa đổi dữ liệu bằng mã của họ.

Việc xử lý một ý định đến nhằm sửa đổi dữ liệu của trình cung cấp của bạn không khác với việc xử lý các ý định khác. Bạn có thể tìm hiểu về việc sử dụng ý định bằng cách đọc chủ đề Ý định và Bộ lọc Ý định.