Trình cung cấp danh bạ

Contacts Provider là một thành phần mạnh mẽ và linh hoạt của Android, có chức năng quản lý kho lưu trữ dữ liệu trung tâm của thiết bị về mọi người. Contacts Provider là nguồn dữ liệu mà bạn thấy trong ứng dụng danh bạ của thiết bị. Bạn cũng có thể truy cập vào dữ liệu của ứng dụng này trong ứng dụng của riêng mình và chuyển dữ liệu giữa thiết bị và các dịch vụ trực tuyến. Nhà cung cấp này hỗ trợ nhiều nguồn dữ liệu và cố gắng quản lý nhiều dữ liệu nhất có thể cho mỗi người, dẫn đến việc tổ chức của nhà cung cấp này rất phức tạp. Do đó, API của nhà cung cấp có một tập hợp lớn các lớp hợp đồng và giao diện giúp cả việc truy xuất và sửa đổi dữ liệu.

Hướng dẫn này mô tả những nội dung sau:

  • Cấu trúc cơ bản của nhà cung cấp.
  • Cách truy xuất dữ liệu từ trình cung cấp.
  • Cách sửa đổi dữ liệu trong nhà cung cấp.
  • Cách viết một bộ chuyển đổi đồng bộ hoá để đồng bộ hoá dữ liệu từ máy chủ của bạn với Trình cung cấp danh bạ.

Hướng dẫn này giả định rằng bạn đã nắm được những kiến thức cơ bản về nhà cung cấp nội dung Android. Để tìm hiểu thêm về trình cung cấp nội dung Android, hãy đọc hướng dẫn Các khái niệm cơ bản về trình cung cấp nội dung.

Tổ chức Trình cung cấp danh bạ

Trình cung cấp danh bạ là một thành phần trình cung cấp nội dung của Android. Nó duy trì 3 loại dữ liệu về một người, mỗi loại tương ứng với một bảng do nhà cung cấp cung cấp, như minh hoạ trong hình 1:

Hình 1. Cấu trúc bảng Trình cung cấp danh bạ.

Ba bảng này thường được gọi bằng tên của các lớp hợp đồng. Các lớp này xác định hằng số cho URI nội dung, tên cột và giá trị cột mà các bảng sử dụng:

ContactsContract.Contacts bảng
Các hàng đại diện cho những người khác nhau, dựa trên dữ liệu tổng hợp của các hàng liên hệ thô.
ContactsContract.RawContacts bảng
Các hàng chứa thông tin tóm tắt về dữ liệu của một người, cụ thể theo tài khoản và loại người dùng.
ContactsContract.Data bảng
Các hàng chứa thông tin chi tiết về người liên hệ thô, chẳng hạn như địa chỉ email hoặc số điện thoại.

Các bảng khác do các lớp hợp đồng đại diện trong ContactsContract là các bảng phụ mà Trình cung cấp danh bạ dùng để quản lý các hoạt động hoặc hỗ trợ các chức năng cụ thể trong ứng dụng danh bạ hoặc ứng dụng điện thoại của thiết bị.

Liên hệ thô

Một số liên hệ thô đại diện cho dữ liệu của một người đến từ một loại tài khoản và tên tài khoản duy nhất. Vì Trình cung cấp danh bạ cho phép nhiều dịch vụ trực tuyến làm nguồn dữ liệu cho một người, nên Trình cung cấp danh bạ cho phép nhiều danh bạ thô cho cùng một người. Nhiều danh bạ thô cũng cho phép người dùng kết hợp dữ liệu của một người từ nhiều tài khoản thuộc cùng một loại tài khoản.

Hầu hết dữ liệu cho một người liên hệ thô đều không được lưu trữ trong bảng ContactsContract.RawContacts. Thay vào đó, thông tin này được lưu trữ trong một hoặc nhiều hàng trong bảng ContactsContract.Data. Mỗi hàng dữ liệu có một cột Data.RAW_CONTACT_ID chứa giá trị RawContacts._ID của hàng ContactsContract.RawContacts mẹ.

Các cột quan trọng của danh bạ thô

Các cột quan trọng trong bảng ContactsContract.RawContacts được liệt kê trong bảng 1. Vui lòng đọc các ghi chú sau bảng:

Bảng 1. Các cột quan trọng của danh bạ thô.

Tên cột Mục đích sử dụng Ghi chú
ACCOUNT_NAME Tên tài khoản cho loại tài khoản là nguồn của người liên hệ thô này. Ví dụ: tên tài khoản của Tài khoản Google là một trong những địa chỉ Gmail của chủ sở hữu thiết bị. Hãy xem mục tiếp theo để biết thêm thông tin về ACCOUNT_TYPE. Định dạng của tên này dành riêng cho loại tài khoản. Đây không nhất thiết phải là địa chỉ email.
ACCOUNT_TYPE Loại tài khoản là nguồn của người liên hệ thô này. Ví dụ: loại tài khoản của một Tài khoản Google là com.google. Luôn xác định loại tài khoản của bạn bằng một mã nhận dạng miền cho miền mà bạn sở hữu hoặc kiểm soát. Điều này sẽ đảm bảo rằng loại tài khoản của bạn là duy nhất. Loại tài khoản cung cấp dữ liệu danh bạ thường có một bộ điều hợp đồng bộ hoá được liên kết. Bộ điều hợp này sẽ đồng bộ hoá với Trình cung cấp danh bạ.
DELETED Cờ "đã xoá" cho một người liên hệ thô. Cờ này cho phép Trình cung cấp danh bạ duy trì hàng nội bộ cho đến khi các bộ điều hợp đồng bộ hoá có thể xoá hàng khỏi máy chủ của chúng, rồi cuối cùng xoá hàng khỏi kho lưu trữ.

Ghi chú

Sau đây là những lưu ý quan trọng về bảng ContactsContract.RawContacts:

  • Tên của một người liên hệ thô không được lưu trữ trong hàng của người đó trong ContactsContract.RawContacts. Thay vào đó, thông tin này được lưu trữ trong bảng ContactsContract.Data, trong hàng ContactsContract.CommonDataKinds.StructuredName. Một người liên hệ thô chỉ có một hàng thuộc loại này trong bảng ContactsContract.Data.
  • Thận trọng: Để sử dụng dữ liệu tài khoản của riêng bạn trong một hàng liên hệ thô, trước tiên, dữ liệu đó phải được đăng ký bằng AccountManager. Để thực hiện việc này, hãy nhắc người dùng thêm loại tài khoản và tên tài khoản của họ vào danh sách tài khoản. Nếu bạn không làm như vậy, Trình cung cấp danh bạ sẽ tự động xoá hàng liên hệ thô của bạn.

    Ví dụ: nếu bạn muốn ứng dụng của mình duy trì dữ liệu danh bạ cho dịch vụ dựa trên web có miền com.example.dataservice và tài khoản của người dùng cho dịch vụ của bạn là becky.sharp@dataservice.example.com, thì trước tiên, người dùng phải thêm "loại" tài khoản (com.example.dataservice) và "tên" tài khoản (becky.smart@dataservice.example.com) thì ứng dụng của bạn mới có thể thêm các hàng liên hệ thô. Bạn có thể giải thích yêu cầu này cho người dùng trong tài liệu hoặc bạn có thể nhắc người dùng thêm loại và tên hoặc cả hai. Các loại tài khoản và tên tài khoản được mô tả chi tiết hơn trong phần tiếp theo.

Nguồn dữ liệu về danh bạ thô

Để hiểu cách hoạt động của danh bạ thô, hãy xem xét người dùng "Emily Dickinson" có 3 tài khoản người dùng sau đây được xác định trên thiết bị của cô ấy:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Tài khoản Twitter "belle_of_amherst"

Người dùng này đã bật chế độ Đồng bộ hoá danh bạ cho cả 3 tài khoản này trong phần cài đặt Tài khoản.

Giả sử Emily Dickinson mở một cửa sổ trình duyệt, đăng nhập vào Gmail với tư cách là emily.dickinson@gmail.com, mở Danh bạ rồi thêm "Thomas Higginson". Sau đó, cô ấy đăng nhập vào Gmail với tư cách là emilyd@gmail.com và gửi email cho "Thomas Higginson". Hệ thống sẽ tự động thêm anh ấy làm người liên hệ. Cô cũng theo dõi "colonel_tom" (mã nhận dạng Twitter của Thomas Higginson) trên Twitter.

Trình cung cấp danh bạ tạo ra 3 danh bạ thô do kết quả của thao tác này:

  1. Một người liên hệ thô cho "Thomas Higginson" được liên kết với emily.dickinson@gmail.com. Loại tài khoản người dùng là Google.
  2. Một số điện thoại thô thứ hai cho "Thomas Higginson" được liên kết với emilyd@gmail.com. Loại tài khoản người dùng cũng là Google. Có một số liên hệ thô thứ hai mặc dù tên giống với tên trước đó, vì người đó được thêm vào một tài khoản người dùng khác.
  3. Người liên hệ thô thứ ba cho "Thomas Higginson" được liên kết với "belle_of_amherst". Loại tài khoản người dùng là Twitter.

Dữ liệu

Như đã lưu ý trước đó, dữ liệu cho một số điện thoại liên hệ thô được lưu trữ trong hàng ContactsContract.Data được liên kết với giá trị _ID của số điện thoại liên hệ thô. Điều này cho phép một số liên hệ thô có nhiều thực thể của cùng một loại dữ liệu, chẳng hạn như địa chỉ email hoặc số điện thoại. Ví dụ: nếu "Thomas Higginson" cho emilyd@gmail.com (hàng liên hệ thô cho Thomas Higginson được liên kết với Tài khoản Google emilyd@gmail.com) có địa chỉ email nhà riêng là thigg@gmail.com và địa chỉ email nơi làm việc là thomas.higginson@gmail.com, thì Trình cung cấp danh bạ sẽ lưu trữ hai hàng địa chỉ email và liên kết cả hai với người liên hệ thô.

Lưu ý rằng các loại dữ liệu khác nhau được lưu trữ trong bảng duy nhất này. Tên hiển thị, số điện thoại, email, địa chỉ gửi thư, ảnh và các hàng chi tiết về trang web đều nằm trong bảng ContactsContract.Data. Để giúp quản lý việc này, bảng ContactsContract.Data có một số cột có tên mô tả và một số cột khác có tên chung. Nội dung của cột tên mô tả có cùng ý nghĩa bất kể loại dữ liệu trong hàng, trong khi nội dung của cột tên chung có ý nghĩa khác nhau tuỳ thuộc vào loại dữ liệu.

Tên cột mô tả

Sau đây là một số ví dụ về tên cột mô tả:

RAW_CONTACT_ID
Giá trị của cột _ID trong danh bạ thô cho dữ liệu này.
MIMETYPE
Loại dữ liệu được lưu trữ trong hàng này, được biểu thị dưới dạng một loại MIME tuỳ chỉnh. Trình cung cấp danh bạ sử dụng các loại MIME được xác định trong các lớp con của ContactsContract.CommonDataKinds. Các loại MIME này là mã nguồn mở và có thể được dùng bởi mọi ứng dụng hoặc bộ chuyển đổi đồng bộ hoá hoạt động với Trình cung cấp danh bạ.
IS_PRIMARY
Nếu loại hàng dữ liệu này có thể xuất hiện nhiều lần cho một người liên hệ thô, thì cột IS_PRIMARY sẽ gắn cờ hàng dữ liệu chứa dữ liệu chính cho loại đó. Ví dụ: nếu người dùng nhấn và giữ một số điện thoại của một người liên hệ rồi chọn Đặt làm mặc định, thì hàng ContactsContract.Data chứa số đó sẽ có cột IS_PRIMARY được đặt thành một giá trị khác 0.

Tên cột chung

Có 15 cột chung có tên từ DATA1 đến DATA15 thường có sẵn và 4 cột chung khác từ SYNC1 đến SYNC4 chỉ nên được dùng bởi các bộ chuyển đổi đồng bộ hoá. Các hằng số tên cột chung luôn hoạt động, bất kể loại dữ liệu mà hàng chứa.

Cột DATA1 đã được lập chỉ mục. Trình cung cấp danh bạ luôn sử dụng cột này cho dữ liệu mà trình cung cấp dự kiến sẽ là mục tiêu thường xuyên nhất của một truy vấn. Ví dụ: trong một hàng email, cột này chứa địa chỉ email thực tế.

Theo quy ước, cột DATA15 được dành riêng để lưu trữ dữ liệu Đối tượng nhị phân lớn (BLOB), chẳng hạn như hình thu nhỏ của ảnh.

Tên cột theo loại

Để tạo điều kiện thuận lợi cho việc xử lý các cột cho một loại hàng cụ thể, Trình cung cấp danh bạ cũng cung cấp các hằng số tên cột dành riêng cho loại, được xác định trong các lớp con của ContactsContract.CommonDataKinds. Các hằng số chỉ đơn giản là đặt một tên hằng số khác cho cùng một tên cột, giúp bạn truy cập vào dữ liệu trong một hàng thuộc một loại cụ thể.

Ví dụ: lớp ContactsContract.CommonDataKinds.Email xác định các hằng số tên cột dành riêng cho loại cho một hàng ContactsContract.Data có loại MIME Email.CONTENT_ITEM_TYPE. Lớp này chứa hằng số ADDRESS cho cột địa chỉ email. Giá trị thực tế của ADDRESS là "data1", giống với tên chung của cột.

Thận trọng: Đừng thêm dữ liệu tuỳ chỉnh của riêng bạn vào bảng ContactsContract.Data bằng cách sử dụng một hàng có một trong các loại MIME được xác định trước của trình cung cấp. Nếu làm vậy, bạn có thể mất dữ liệu hoặc khiến trình cung cấp bị trục trặc. Ví dụ: bạn không nên thêm một hàng có loại MIME Email.CONTENT_ITEM_TYPE chứa tên người dùng thay vì địa chỉ email trong cột DATA1. Nếu sử dụng loại MIME tuỳ chỉnh của riêng mình cho hàng, thì bạn có thể tự do xác định tên cột dành riêng cho loại và sử dụng các cột theo ý muốn.

Hình 2 cho thấy cách các cột mô tả và cột dữ liệu xuất hiện trong một hàng ContactsContract.Data, cũng như cách tên cột dành riêng cho loại "lớp phủ" tên cột chung

Cách ánh xạ tên cột theo loại với tên cột chung

Hình 2. Tên cột theo loại và tên cột chung.

Các lớp tên cột theo loại

Bảng 2 liệt kê các lớp tên cột dành riêng cho loại thường dùng nhất:

Bảng 2. Các lớp tên cột theo loại

Lớp ánh xạ Loại dữ liệu Ghi chú
ContactsContract.CommonDataKinds.StructuredName Dữ liệu tên cho người liên hệ thô được liên kết với hàng dữ liệu này. Một người liên hệ thô chỉ có một trong các hàng này.
ContactsContract.CommonDataKinds.Photo Ảnh chính của người liên hệ thô được liên kết với hàng dữ liệu này. Một người liên hệ thô chỉ có một trong các hàng này.
ContactsContract.CommonDataKinds.Email Địa chỉ email của người liên hệ thô được liên kết với hàng dữ liệu này. Một người liên hệ thô có thể có nhiều địa chỉ email.
ContactsContract.CommonDataKinds.StructuredPostal Địa chỉ bưu chính của người liên hệ thô được liên kết với hàng dữ liệu này. Một danh bạ thô có thể có nhiều địa chỉ bưu chính.
ContactsContract.CommonDataKinds.GroupMembership Giá trị nhận dạng liên kết người liên hệ thô với một trong các nhóm trong Trình cung cấp danh bạ. Nhóm là một tính năng không bắt buộc của loại tài khoản và tên tài khoản. Các nhóm này được mô tả chi tiết hơn trong phần Nhóm liên hệ.

Danh bạ

Trình cung cấp danh bạ kết hợp các hàng liên hệ thô trên tất cả các loại tài khoản và tên tài khoản để tạo thành một liên hệ. Điều này giúp hiển thị và sửa đổi tất cả dữ liệu mà người dùng đã thu thập cho một người. Trình cung cấp danh bạ quản lý việc tạo các hàng liên hệ mới và việc tổng hợp các liên hệ thô với một hàng liên hệ hiện có. Cả ứng dụng lẫn bộ điều hợp đồng bộ hoá đều không được phép thêm người liên hệ và một số cột trong hàng người liên hệ là chỉ đọc.

Lưu ý: Nếu cố gắng thêm một người liên hệ vào Trình cung cấp danh bạ bằng insert(), bạn sẽ nhận được một ngoại lệ UnsupportedOperationException. Nếu bạn cố gắng cập nhật một cột được liệt kê là "chỉ đọc", thì nội dung cập nhật sẽ bị bỏ qua.

Trình cung cấp danh bạ sẽ tạo một người liên hệ mới để phản hồi việc thêm một người liên hệ thô mới không khớp với bất kỳ người liên hệ hiện có nào. Nhà cung cấp cũng làm như vậy nếu dữ liệu của một số liên hệ thô hiện có thay đổi theo cách mà dữ liệu đó không còn khớp với số liên hệ mà dữ liệu đó được đính kèm trước đó. Nếu một ứng dụng hoặc trình điều hợp đồng bộ hoá tạo một người liên hệ thô mới không khớp với một người liên hệ hiện có, thì người liên hệ thô mới sẽ được tổng hợp vào người liên hệ hiện có.

Trình cung cấp danh bạ liên kết một hàng danh bạ với các hàng danh bạ thô bằng cột _ID của hàng danh bạ trong bảng Contacts. Cột CONTACT_ID của bảng danh bạ thô ContactsContract.RawContacts chứa các giá trị _ID cho hàng danh bạ được liên kết với mỗi hàng danh bạ thô.

Bảng ContactsContract.Contacts cũng có cột LOOKUP_KEY là đường liên kết "vĩnh viễn" đến hàng liên hệ. Vì Trình cung cấp danh bạ tự động duy trì danh bạ, nên trình này có thể thay đổi giá trị _ID của một hàng danh bạ để phản hồi một hoạt động tổng hợp hoặc đồng bộ hoá. Ngay cả khi điều này xảy ra, URI nội dung CONTENT_LOOKUP_URI kết hợp với LOOKUP_KEY của người liên hệ vẫn sẽ trỏ đến hàng người liên hệ, vì vậy, bạn có thể sử dụng LOOKUP_KEY để duy trì các đường liên kết đến người liên hệ "yêu thích", v.v. Cột này có định dạng riêng, không liên quan đến định dạng của cột _ID.

Hình 3 cho thấy mối quan hệ giữa 3 bảng chính.

Các bảng chính của trình cung cấp danh bạ

Hình 3. Mối quan hệ giữa các bảng Danh bạ, Danh bạ thô và Thông tin chi tiết.

Thận trọng: Nếu bạn xuất bản ứng dụng lên Cửa hàng Google Play hoặc nếu ứng dụng của bạn nằm trên một thiết bị chạy Android 10 (API cấp 29) trở lên, hãy lưu ý rằng một số ít trường dữ liệu và phương thức liên hệ đã lỗi thời.

Trong các điều kiện đã đề cập, hệ thống sẽ định kỳ xoá mọi giá trị được ghi vào các trường dữ liệu này:

Các API dùng để đặt các trường dữ liệu nêu trên cũng không còn được dùng nữa:

Ngoài ra, các trường sau đây không còn trả về người liên hệ thường xuyên. Lưu ý rằng một số trường trong số này chỉ ảnh hưởng đến thứ hạng của các liên hệ khi các liên hệ đó thuộc một loại dữ liệu cụ thể.

Nếu ứng dụng của bạn đang truy cập hoặc cập nhật các trường hoặc API này, hãy sử dụng các phương thức thay thế. Ví dụ: bạn có thể đáp ứng một số trường hợp sử dụng nhất định bằng cách dùng trình cung cấp nội dung riêng tư hoặc dữ liệu khác được lưu trữ trong ứng dụng hoặc hệ thống phụ trợ của bạn.

Để xác minh rằng chức năng của ứng dụng không bị ảnh hưởng bởi thay đổi này, bạn có thể xoá các trường dữ liệu này theo cách thủ công. Để làm như vậy, hãy chạy lệnh ADB sau trên thiết bị chạy Android 4.1 (API cấp 16) trở lên:

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

Dữ liệu từ bộ điều hợp đồng bộ hoá

Người dùng nhập dữ liệu liên hệ trực tiếp vào thiết bị, nhưng dữ liệu cũng được chuyển vào Trình cung cấp danh bạ từ các dịch vụ web thông qua bộ điều hợp đồng bộ hoá. Bộ điều hợp này tự động hoá việc chuyển dữ liệu giữa thiết bị và các dịch vụ. Trình điều hợp đồng bộ hoá chạy ở chế độ nền dưới sự kiểm soát của hệ thống và gọi các phương thức ContentResolver để quản lý dữ liệu.

Trong Android, dịch vụ web mà một trình điều hợp đồng bộ hoá hoạt động được xác định bằng một loại tài khoản. Mỗi trình điều hợp đồng bộ hoá hoạt động với một loại tài khoản, nhưng có thể hỗ trợ nhiều tên tài khoản cho loại đó. Các loại tài khoản và tên tài khoản được mô tả ngắn gọn trong phần Nguồn dữ liệu danh bạ thô. Các định nghĩa sau đây cung cấp thêm thông tin chi tiết và mô tả cách loại tài khoản và tên tài khoản liên quan đến các dịch vụ và bộ điều hợp đồng bộ hoá.

Loại tài khoản
Xác định một dịch vụ mà người dùng đã lưu trữ dữ liệu. Hầu hết thời gian, người dùng phải xác thực bằng dịch vụ. Ví dụ: Danh bạ Google là một loại tài khoản, được xác định bằng mã google.com. Giá trị này tương ứng với loại tài khoản mà AccountManager sử dụng.
Tên tài khoản
Xác định một tài khoản hoặc thông tin đăng nhập cụ thể cho một loại tài khoản. Tài khoản Google Danh bạ cũng giống như Tài khoản Google, có địa chỉ email làm tên tài khoản. Các dịch vụ khác có thể sử dụng tên người dùng một từ hoặc mã nhận dạng bằng số.

Các loại tài khoản không nhất thiết phải là duy nhất. Người dùng có thể định cấu hình nhiều tài khoản Google Danh bạ và tải dữ liệu xuống Trình cung cấp danh bạ; điều này có thể xảy ra nếu người dùng có một nhóm danh bạ cá nhân cho tên tài khoản cá nhân và một nhóm khác cho công việc. Tên tài khoản thường là duy nhất. Cùng với nhau, chúng xác định một luồng dữ liệu cụ thể giữa Trình cung cấp danh bạ và một dịch vụ bên ngoài.

Nếu muốn chuyển dữ liệu của dịch vụ sang Trình cung cấp danh bạ, bạn cần viết trình điều hợp đồng bộ hoá của riêng mình. Điều này được mô tả chi tiết hơn trong phần Bộ điều hợp đồng bộ hoá của Trình cung cấp danh bạ.

Hình 4 cho thấy cách Contacts Provider phù hợp với luồng dữ liệu về mọi người. Trong hộp có đánh dấu "sync adapters" (bộ chuyển đổi đồng bộ hoá), mỗi bộ chuyển đổi được gắn nhãn theo loại tài khoản.

Luồng dữ liệu về người dùng

Hình 4. Luồng dữ liệu của Trình cung cấp danh bạ.

Các quyền bắt buộc

Những ứng dụng muốn truy cập vào Trình cung cấp danh bạ phải yêu cầu các quyền sau:

Quyền đọc đối với một hoặc nhiều bảng
READ_CONTACTS, được chỉ định trong AndroidManifest.xml bằng phần tử <uses-permission> dưới dạng <uses-permission android:name="android.permission.READ_CONTACTS">.
Quyền ghi vào một hoặc nhiều bảng
WRITE_CONTACTS, được chỉ định trong AndroidManifest.xml bằng phần tử <uses-permission> dưới dạng <uses-permission android:name="android.permission.WRITE_CONTACTS">.

Các quyền này không áp dụng cho dữ liệu hồ sơ người dùng. Hồ sơ người dùng và các quyền bắt buộc của hồ sơ này sẽ được thảo luận trong phần sau, Hồ sơ người dùng.

Hãy nhớ rằng dữ liệu về người liên hệ của người dùng là dữ liệu riêng tư và nhạy cảm. Người dùng lo ngại về quyền riêng tư của họ, vì vậy họ không muốn các ứng dụng thu thập dữ liệu về họ hoặc danh bạ của họ. Nếu không rõ lý do bạn cần quyền truy cập vào dữ liệu danh bạ của họ, họ có thể đánh giá ứng dụng của bạn ở mức thấp hoặc đơn giản là từ chối cài đặt ứng dụng.

Hồ sơ người dùng

Bảng ContactsContract.Contacts có một hàng duy nhất chứa dữ liệu hồ sơ cho người dùng thiết bị. Dữ liệu này mô tả user của thiết bị chứ không phải một trong các số liên hệ của người dùng. Hàng người liên hệ trong hồ sơ được liên kết với một hàng người liên hệ thô cho mỗi hệ thống sử dụng hồ sơ. Mỗi hàng người liên hệ thô trong hồ sơ có thể có nhiều hàng dữ liệu. Các hằng số để truy cập vào hồ sơ người dùng có trong lớp ContactsContract.Profile.

Bạn cần có quyền đặc biệt để truy cập vào hồ sơ người dùng. Ngoài các quyền READ_CONTACTSWRITE_CONTACTS cần thiết để đọc và ghi, quyền truy cập vào hồ sơ người dùng yêu cầu các quyền android.Manifest.permission#READ_PROFILE và android.Manifest.permission#WRITE_PROFILE để lần lượt đọc và ghi.

Hãy nhớ rằng bạn nên coi hồ sơ người dùng là thông tin nhạy cảm. Quyền android.Manifest.permission#READ_PROFILE cho phép bạn truy cập vào dữ liệu nhận dạng cá nhân của người dùng thiết bị. Hãy nhớ cho người dùng biết lý do bạn cần quyền truy cập vào hồ sơ người dùng trong phần mô tả ứng dụng.

Để truy xuất hàng liên hệ chứa hồ sơ của người dùng, hãy gọi ContentResolver.query(). Đặt URI nội dung thành CONTENT_URI và không cung cấp bất kỳ tiêu chí lựa chọn nào. Bạn cũng có thể dùng URI nội dung này làm URI cơ sở để truy xuất dữ liệu hoặc danh bạ thô cho hồ sơ. Ví dụ: đoạn mã này truy xuất dữ liệu cho hồ sơ:

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

// Sets the columns to retrieve for the user profile
projection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
profileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                projection ,
                null,
                null,
                null);

Lưu ý: Nếu bạn truy xuất nhiều hàng liên hệ và muốn xác định xem một trong số đó có phải là hồ sơ người dùng hay không, hãy kiểm tra cột IS_USER_PROFILE của hàng. Cột này được đặt thành "1" nếu người liên hệ là hồ sơ người dùng.

Siêu dữ liệu của Trình cung cấp danh bạ

Trình cung cấp danh bạ quản lý dữ liệu theo dõi trạng thái của dữ liệu danh bạ trong kho lưu trữ. Siêu dữ liệu này về kho lưu trữ được lưu trữ ở nhiều nơi, bao gồm các hàng trong bảng Raw Contacts, Data và Contacts, bảng ContactsContract.Settings và bảng ContactsContract.SyncState. Bảng sau đây cho biết ảnh hưởng của từng phần siêu dữ liệu này:

Bảng 3. Siêu dữ liệu trong Trình cung cấp danh bạ

Bảng Cột Giá trị Ý nghĩa
ContactsContract.RawContacts DIRTY "0" – không thay đổi kể từ lần đồng bộ hoá gần đây nhất. Đánh dấu những người liên hệ thô đã được thay đổi trên thiết bị và phải được đồng bộ hoá trở lại máy chủ. Giá trị này được Trình cung cấp danh bạ tự động đặt khi các ứng dụng Android cập nhật một hàng.

Các bộ điều hợp đồng bộ hoá sửa đổi bảng dữ liệu hoặc bảng liên hệ thô phải luôn thêm chuỗi CALLER_IS_SYNCADAPTER vào URI nội dung mà chúng sử dụng. Điều này ngăn nhà cung cấp đánh dấu các hàng là không hợp lệ. Nếu không, các nội dung sửa đổi của bộ điều hợp đồng bộ hoá sẽ xuất hiện dưới dạng nội dung sửa đổi cục bộ và được gửi đến máy chủ, ngay cả khi máy chủ là nguồn của nội dung sửa đổi.

"1" – đã thay đổi kể từ lần đồng bộ hoá gần đây nhất, cần được đồng bộ hoá lại với máy chủ.
ContactsContract.RawContacts VERSION Số phiên bản của hàng này. Trình cung cấp danh bạ sẽ tự động tăng giá trị này bất cứ khi nào hàng hoặc dữ liệu liên quan của hàng thay đổi.
ContactsContract.Data DATA_VERSION Số phiên bản của hàng này. Trình cung cấp danh bạ sẽ tự động tăng giá trị này bất cứ khi nào hàng dữ liệu thay đổi.
ContactsContract.RawContacts SOURCE_ID Một giá trị chuỗi xác định duy nhất mối liên hệ thô này với tài khoản mà mối liên hệ đó được tạo. Khi bộ chuyển đổi đồng bộ hoá tạo một người liên hệ thô mới, cột này phải được đặt thành mã nhận dạng duy nhất của máy chủ cho người liên hệ thô. Khi một ứng dụng Android tạo một người liên hệ thô mới, ứng dụng đó phải để trống cột này. Thao tác này báo hiệu cho bộ điều hợp đồng bộ rằng bộ điều hợp này phải tạo một số điện thoại liên hệ thô mới trên máy chủ và nhận một giá trị cho SOURCE_ID.

Cụ thể, mã nhận dạng nguồn phải là duy nhất cho mỗi loại tài khoản và phải ổn định trong quá trình đồng bộ hoá:

  • Riêng biệt: Mỗi số liên lạc thô cho một tài khoản phải có mã nguồn riêng. Nếu không thực thi quy tắc này, bạn sẽ gây ra vấn đề trong ứng dụng danh bạ. Xin lưu ý rằng hai danh bạ thô cho cùng một loại tài khoản có thể có cùng mã nguồn. Ví dụ: danh bạ thô "Thomas Higginson" cho tài khoản emily.dickinson@gmail.com được phép có cùng mã nhận dạng nguồn với danh bạ thô "Thomas Higginson" cho tài khoản emilyd@gmail.com.
  • Ổn định: Mã nguồn là một phần vĩnh viễn trong dữ liệu của dịch vụ trực tuyến cho danh bạ thô. Ví dụ: nếu người dùng xoá Bộ nhớ danh bạ khỏi phần Cài đặt ứng dụng và đồng bộ hoá lại, thì các danh bạ thô đã khôi phục phải có cùng mã nguồn như trước. Nếu bạn không thực thi chế độ cài đặt này, các lối tắt sẽ ngừng hoạt động.
ContactsContract.Groups GROUP_VISIBLE "0" – Các mục liên hệ trong nhóm này sẽ không xuất hiện trong giao diện người dùng của ứng dụng Android. Cột này dùng để tương thích với những máy chủ cho phép người dùng ẩn địa chỉ liên hệ trong một số nhóm nhất định.
"1" – Các địa chỉ liên hệ trong nhóm này được phép xuất hiện trong giao diện người dùng của ứng dụng.
ContactsContract.Settings UNGROUPED_VISIBLE "0" – Đối với tài khoản và loại tài khoản này, những người liên hệ không thuộc một nhóm sẽ không xuất hiện trên giao diện người dùng của ứng dụng Android. Theo mặc định, người liên hệ sẽ không xuất hiện nếu không có người liên hệ thô nào thuộc một nhóm (Tư cách thành viên nhóm của một người liên hệ thô được biểu thị bằng một hoặc nhiều hàng ContactsContract.CommonDataKinds.GroupMembership trong bảng ContactsContract.Data). Bằng cách đặt cờ này trong hàng của bảng ContactsContract.Settings cho một loại tài khoản và tài khoản, bạn có thể buộc những người liên hệ không có nhóm xuất hiện. Một trường hợp sử dụng cờ này là để hiện các liên hệ từ những máy chủ không dùng nhóm.
"1" – Đối với tài khoản và loại tài khoản này, những người liên hệ không thuộc nhóm sẽ hiển thị trên giao diện người dùng của ứng dụng.
ContactsContract.SyncState (tất cả) Hãy dùng bảng này để lưu trữ siêu dữ liệu cho bộ điều hợp đồng bộ hoá. Với bảng này, bạn có thể liên tục lưu trữ trạng thái đồng bộ hoá và các dữ liệu khác liên quan đến hoạt động đồng bộ hoá trên thiết bị.

Quyền truy cập vào Trình cung cấp danh bạ

Phần này mô tả các nguyên tắc truy cập vào dữ liệu từ Trình cung cấp danh bạ, tập trung vào những nội dung sau:

  • Cụm từ tìm kiếm về thực thể.
  • Sửa đổi hàng loạt.
  • Truy xuất và sửa đổi bằng ý định.
  • Tính toàn vẹn của dữ liệu.

Việc sửa đổi từ một bộ điều hợp đồng bộ hoá cũng được đề cập chi tiết hơn trong phần Bộ điều hợp đồng bộ hoá Trình cung cấp danh bạ.

Truy vấn thực thể

Vì các bảng Trình cung cấp danh bạ được sắp xếp theo hệ phân cấp, nên thường hữu ích khi truy xuất một hàng và tất cả các hàng "con" được liên kết với hàng đó. Ví dụ: để hiển thị tất cả thông tin của một người, bạn có thể muốn truy xuất tất cả các hàng ContactsContract.RawContacts cho một hàng ContactsContract.Contacts hoặc tất cả các hàng ContactsContract.CommonDataKinds.Email cho một hàng ContactsContract.RawContacts. Để tạo điều kiện thuận lợi cho việc này, Contacts Provider cung cấp các cấu trúc thực thể, hoạt động như các thao tác kết hợp cơ sở dữ liệu giữa các bảng.

Thực thể giống như một bảng bao gồm các cột được chọn trong bảng mẹ và bảng con của bảng đó. Khi truy vấn một thực thể, bạn sẽ cung cấp một phép chiếu và tiêu chí tìm kiếm dựa trên các cột có sẵn của thực thể đó. Kết quả là một Cursor chứa một hàng cho mỗi hàng trong bảng con đã được truy xuất. Ví dụ: nếu bạn truy vấn ContactsContract.Contacts.Entity cho tên người liên hệ và tất cả các hàng ContactsContract.CommonDataKinds.Email cho tất cả các số điện thoại liên hệ thô cho tên đó, bạn sẽ nhận được Cursor chứa một hàng cho mỗi hàng ContactsContract.CommonDataKinds.Email.

Các thực thể giúp đơn giản hoá truy vấn. Khi sử dụng một thực thể, bạn có thể truy xuất tất cả dữ liệu liên hệ cho một người liên hệ hoặc người liên hệ thô cùng một lúc, thay vì phải truy vấn bảng mẹ trước để lấy mã nhận dạng, rồi phải truy vấn bảng con bằng mã nhận dạng đó. Ngoài ra, Trình cung cấp danh bạ sẽ xử lý một truy vấn đối với một thực thể trong một giao dịch duy nhất, điều này đảm bảo rằng dữ liệu đã truy xuất nhất quán về mặt nội bộ.

Lưu ý: Thông thường, một thực thể không chứa tất cả các cột của bảng mẹ và bảng con. Nếu cố gắng sử dụng một tên cột không có trong danh sách hằng số tên cột cho thực thể, bạn sẽ nhận được một Exception.

Đoạn mã sau đây cho biết cách truy xuất tất cả các hàng liên hệ thô cho một người liên hệ. Đoạn mã này là một phần của một ứng dụng lớn hơn có 2 hoạt động là "main" và "detail". Hoạt động chính cho thấy danh sách các hàng liên hệ; khi người dùng chọn một hàng, hoạt động sẽ gửi mã nhận dạng của hàng đó đến hoạt động chi tiết. Hoạt động chi tiết sử dụng ContactsContract.Contacts.Entity để hiển thị tất cả các hàng dữ liệu từ tất cả các số điện thoại liên hệ thô được liên kết với số điện thoại liên hệ đã chọn.

Đoạn mã này được lấy từ hoạt động "chi tiết":

Kotlin

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this        // The context of the activity
    )

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    val projection: Array<String> = arrayOf(
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
    )

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC"

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // The entity content URI for a single contact
            projection,         // The columns to retrieve
            null,               // Retrieve all the raw contacts and their data rows.
            null,               //
            sortOrder           // Sort by the raw contact ID.
    )
}

Java

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            contactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

Khi quá trình tải hoàn tất, LoaderManager sẽ gọi một lệnh gọi lại đến onLoadFinished(). Một trong các đối số đến của phương thức này là Cursor có kết quả của truy vấn. Trong ứng dụng của riêng mình, bạn có thể lấy dữ liệu từ Cursor này để hiển thị hoặc xử lý thêm.

Sửa đổi hàng loạt

Bất cứ khi nào có thể, bạn nên chèn, cập nhật và xoá dữ liệu trong Trình cung cấp danh bạ ở "chế độ hàng loạt", bằng cách tạo một ArrayList gồm các đối tượng ContentProviderOperation và gọi applyBatch(). Vì Trình cung cấp danh bạ thực hiện tất cả các thao tác trong applyBatch() trong một giao dịch duy nhất, nên các nội dung sửa đổi của bạn sẽ không bao giờ rời khỏi kho lưu trữ danh bạ ở trạng thái không nhất quán. Một sửa đổi hàng loạt cũng giúp việc chèn một số lượng lớn người liên hệ và dữ liệu chi tiết của họ cùng một lúc.

Lưu ý: Để sửa đổi một số liên hệ thô duy nhất, hãy cân nhắc việc gửi một ý định đến ứng dụng danh bạ của thiết bị thay vì xử lý việc sửa đổi trong ứng dụng của bạn. Việc này được mô tả chi tiết hơn trong phần Truy xuất và sửa đổi bằng ý định.

Điểm dừng

Một lần sửa đổi hàng loạt chứa nhiều thao tác có thể chặn các quy trình khác, dẫn đến trải nghiệm tổng thể kém cho người dùng. Để sắp xếp tất cả các nội dung sửa đổi mà bạn muốn thực hiện trong ít danh sách riêng biệt nhất có thể, đồng thời ngăn chúng chặn hệ thống, bạn nên đặt điểm dừng cho một hoặc nhiều thao tác. Điểm dừng là một đối tượng ContentProviderOperation có giá trị isYieldAllowed() được đặt thành true. Khi gặp một điểm dừng, Trình cung cấp danh bạ sẽ tạm dừng hoạt động để cho phép các quy trình khác chạy và đóng giao dịch hiện tại. Khi nhà cung cấp khởi động lại, nhà cung cấp sẽ tiếp tục với thao tác tiếp theo trong ArrayList và bắt đầu một giao dịch mới.

Các điểm năng suất dẫn đến nhiều giao dịch hơn cho mỗi lệnh gọi đến applyBatch(). Do đó, bạn nên đặt một điểm dừng cho thao tác cuối cùng của một nhóm hàng có liên quan. Ví dụ: bạn nên đặt một điểm tạo ra cho thao tác cuối cùng trong một tập hợp thêm các hàng liên hệ thô và các hàng dữ liệu liên kết của nó, hoặc thao tác cuối cùng cho một tập hợp các hàng liên quan đến một liên hệ duy nhất.

Điểm dừng cũng là một đơn vị thao tác ở cấp nguyên tử. Tất cả các quyền truy cập giữa hai điểm dừng sẽ thành công hoặc không thành công dưới dạng một đơn vị duy nhất. Nếu bạn không đặt bất kỳ điểm dừng nào, thì thao tác nguyên tử nhỏ nhất là toàn bộ lô thao tác. Nếu sử dụng điểm dừng, bạn sẽ ngăn các thao tác làm giảm hiệu suất hệ thống, đồng thời đảm bảo rằng một số thao tác là nguyên tử.

Sửa đổi các tham chiếu ngược

Khi chèn một hàng liên hệ thô mới và các hàng dữ liệu liên kết của hàng đó dưới dạng một tập hợp các đối tượng ContentProviderOperation, bạn phải liên kết các hàng dữ liệu với hàng liên hệ thô bằng cách chèn giá trị _ID của liên hệ thô làm giá trị RAW_CONTACT_ID. Tuy nhiên, giá trị này không có sẵn khi bạn đang tạo ContentProviderOperation cho hàng dữ liệu, vì bạn chưa áp dụng ContentProviderOperation cho hàng liên hệ thô. Để giải quyết vấn đề này, lớp ContentProviderOperation.Builder có phương thức withValueBackReference(). Phương thức này cho phép bạn chèn hoặc sửa đổi một cột bằng kết quả của một thao tác trước đó.

Phương thức withValueBackReference() có 2 đối số:

key
Khoá của một cặp khoá-giá trị. Giá trị của đối số này phải là tên của một cột trong bảng mà bạn đang sửa đổi.
previousResult
Chỉ mục bắt đầu từ 0 của một giá trị trong mảng gồm các đối tượng ContentProviderResult từ applyBatch(). Khi các thao tác hàng loạt được áp dụng, kết quả của mỗi thao tác sẽ được lưu trữ trong một mảng kết quả trung gian. Giá trị previousResult là chỉ mục của một trong những kết quả này, được truy xuất và lưu trữ bằng giá trị key. Thao tác này cho phép bạn chèn một bản ghi liên hệ thô mới và nhận lại giá trị _ID của bản ghi đó, sau đó tạo "tham chiếu ngược" đến giá trị khi bạn thêm một hàng ContactsContract.Data.

Toàn bộ mảng kết quả được tạo khi bạn gọi applyBatch() lần đầu tiên, với kích thước bằng kích thước của ArrayList của các đối tượng ContentProviderOperation mà bạn cung cấp. Tuy nhiên, tất cả các phần tử trong mảng kết quả đều được đặt thành null và nếu bạn cố gắng tham chiếu ngược đến một kết quả cho một thao tác chưa được áp dụng, withValueBackReference() sẽ gửi một Exception.

Các đoạn mã sau đây cho biết cách chèn một số lượng lớn dữ liệu và số lượng lớn người liên hệ thô mới. Chúng bao gồm mã thiết lập một điểm tạo và sử dụng một tham chiếu ngược.

Đoạn mã đầu tiên truy xuất dữ liệu liên hệ từ giao diện người dùng. Tại thời điểm này, người dùng đã chọn tài khoản mà bạn sẽ thêm số điện thoại liên hệ thô mới.

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = contactNameEditText.getText().toString();
    String phone = contactPhoneEditText.getText().toString();
    String email = contactEmailEditText.getText().toString();

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

Đoạn mã tiếp theo sẽ tạo một thao tác để chèn hàng liên hệ thô vào bảng ContactsContract.RawContacts:

Kotlin

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

    // Creates a new array of ContentProviderOperation objects.
    val ops = arrayListOf<ContentProviderOperation>()

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Tiếp theo, mã này sẽ tạo các hàng dữ liệu cho tên hiển thị, điện thoại và email.

Mỗi đối tượng trình tạo thao tác đều sử dụng withValueBackReference() để lấy RAW_CONTACT_ID. Các điểm tham chiếu quay lại đối tượng ContentProviderResult từ thao tác đầu tiên, thao tác này sẽ thêm hàng liên hệ thô và trả về giá trị _ID mới của hàng đó. Do đó, mỗi hàng dữ liệu sẽ tự động được liên kết bằng RAW_CONTACT_ID với hàng ContactsContract.RawContacts mới mà hàng đó thuộc về.

Đối tượng ContentProviderOperation.Builder thêm hàng email được gắn cờ bằng withYieldAllowed(), đặt một điểm dừng:

Kotlin

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified phone number and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified email and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Đoạn mã cuối cùng cho thấy lệnh gọi đến applyBatch() để chèn người liên hệ thô và hàng dữ liệu mới.

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

        // Log exception
        Log.e(TAG, "Exception encountered while inserting contact: $e")
    }
}

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

Các thao tác theo lô cũng cho phép bạn triển khai cơ chế kiểm soát đồng thời lạc quan, một phương thức áp dụng các giao dịch sửa đổi mà không cần khoá kho lưu trữ cơ bản. Để sử dụng phương thức này, bạn áp dụng giao dịch rồi kiểm tra các sửa đổi khác có thể đã được thực hiện cùng lúc. Nếu thấy có một điểm sửa đổi không nhất quán, bạn sẽ khôi phục giao dịch và thử lại.

Cơ chế kiểm soát đồng thời lạc quan rất hữu ích cho thiết bị di động, nơi chỉ có một người dùng tại một thời điểm và hiếm khi có quyền truy cập đồng thời vào kho lưu trữ dữ liệu. Vì không sử dụng cơ chế khoá, nên không có thời gian nào bị lãng phí khi thiết lập khoá hoặc chờ các giao dịch khác giải phóng khoá.

Để sử dụng cơ chế kiểm soát đồng thời lạc quan trong khi cập nhật một hàng ContactsContract.RawContacts duy nhất, hãy làm theo các bước sau:

  1. Truy xuất cột VERSION của danh bạ thô cùng với dữ liệu khác mà bạn truy xuất.
  2. Tạo một đối tượng ContentProviderOperation.Builder phù hợp để thực thi một ràng buộc, bằng cách sử dụng phương thức newAssertQuery(Uri). Đối với URI nội dung, hãy dùng RawContacts.CONTENT_URI với _ID của danh bạ thô được thêm vào.
  3. Đối với đối tượng ContentProviderOperation.Builder, hãy gọi withValue() để so sánh cột VERSION với số phiên bản mà bạn vừa truy xuất.
  4. Đối với cùng một ContentProviderOperation.Builder, hãy gọi withExpectedCount() để đảm bảo rằng chỉ có một hàng được kiểm thử bằng câu khẳng định này.
  5. Gọi build() để tạo đối tượng ContentProviderOperation, sau đó thêm đối tượng này làm đối tượng đầu tiên trong ArrayList mà bạn truyền đến applyBatch().
  6. Áp dụng giao dịch hàng loạt.

Nếu hàng liên hệ thô được cập nhật bằng một thao tác khác trong khoảng thời gian bạn đọc hàng và khoảng thời gian bạn cố gắng sửa đổi hàng đó, thì "assert" ContentProviderOperation sẽ không thành công và toàn bộ lô thao tác sẽ được sao lưu. Sau đó, bạn có thể chọn thử lại hàng loạt hoặc thực hiện một số thao tác khác.

Đoạn mã sau đây minh hoạ cách tạo một "assert" ContentProviderOperation sau khi truy vấn một số liên hệ thô duy nhất bằng cách dùng CursorLoader:

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

ops.add(assertOp.build())

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperation>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

Truy xuất và sửa đổi bằng ý định

Việc gửi một ý định đến ứng dụng danh bạ của thiết bị cho phép bạn truy cập gián tiếp vào Trình cung cấp danh bạ. Ý định này khởi động giao diện người dùng của ứng dụng danh bạ trên thiết bị, trong đó người dùng có thể thực hiện các thao tác liên quan đến danh bạ. Với loại quyền truy cập này, người dùng có thể:

  • Chọn một người liên hệ trong danh sách và trả về người liên hệ đó cho ứng dụng của bạn để thực hiện các thao tác khác.
  • Chỉnh sửa dữ liệu của một người liên hệ hiện có.
  • Chèn một người liên hệ thô mới cho bất kỳ tài khoản nào của họ.
  • Xoá một người liên hệ hoặc dữ liệu về người liên hệ.

Nếu người dùng đang chèn hoặc cập nhật dữ liệu, trước tiên, bạn có thể thu thập dữ liệu rồi gửi dữ liệu đó dưới dạng một phần của ý định.

Khi sử dụng các ý định để truy cập vào Trình cung cấp danh bạ thông qua ứng dụng danh bạ của thiết bị, bạn không cần viết giao diện người dùng hoặc mã riêng để truy cập vào trình cung cấp. Bạn cũng không cần phải yêu cầu quyền đọc hoặc ghi vào nhà cung cấp. Ứng dụng danh bạ của thiết bị có thể uỷ quyền đọc cho một người liên hệ cho bạn và vì bạn đang sửa đổi nhà cung cấp thông qua một ứng dụng khác, nên bạn không cần có quyền ghi.

Quy trình chung để gửi một ý định truy cập vào một trình cung cấp được mô tả chi tiết trong hướng dẫn Kiến thức cơ bản về Trình cung cấp nội dung trong phần "Truy cập dữ liệu thông qua ý định". Hành động, loại MIME và giá trị dữ liệu mà bạn sử dụng cho các tác vụ có sẵn được tóm tắt trong Bảng 4, trong khi các giá trị bổ sung mà bạn có thể sử dụng với putExtra() được liệt kê trong tài liệu tham khảo cho ContactsContract.Intents.Insert:

Bảng 4. Ý định của Trình cung cấp danh bạ.

Việc cần làm Thao tác Dữ liệu Loại MIME Ghi chú
Chọn một người liên hệ trong danh sách ACTION_PICK Một trong các lựa chọn sau:
  • Contacts.CONTENT_URI, danh sách này sẽ hiển thị danh sách địa chỉ liên hệ.
  • Phone.CONTENT_URI, hiển thị danh sách số điện thoại của một người liên hệ thô.
  • StructuredPostal.CONTENT_URI, hiển thị danh sách địa chỉ bưu chính của một người liên hệ thô.
  • Email.CONTENT_URI, hiển thị danh sách địa chỉ email của một người liên hệ thô.
Không được sử dụng Hiển thị danh sách các số liên hệ thô hoặc danh sách dữ liệu từ một số liên hệ thô, tuỳ thuộc vào loại URI nội dung mà bạn cung cấp.

Gọi startActivityForResult(), phương thức này sẽ trả về URI nội dung của hàng đã chọn. Dạng của URI là URI nội dung của bảng có LOOKUP_ID của hàng được thêm vào. Ứng dụng danh bạ của thiết bị uỷ quyền đọc và ghi cho URI nội dung này trong suốt thời gian hoạt động của bạn. Hãy xem hướng dẫn Kiến thức cơ bản về Trình cung cấp nội dung để biết thêm thông tin.

Chèn một người liên hệ thô mới Insert.ACTION Không áp dụng RawContacts.CONTENT_TYPE, Loại MIME cho một nhóm các địa chỉ liên hệ thô. Hiển thị màn hình Thêm người liên hệ của ứng dụng danh bạ trên thiết bị. Các giá trị bổ sung mà bạn thêm vào ý định sẽ xuất hiện. Nếu được gửi bằng startActivityForResult(), URI nội dung của số điện thoại liên hệ thô mới thêm sẽ được truyền trở lại phương thức gọi lại onActivityResult() của hoạt động trong đối số Intent, trong trường "data". Để lấy giá trị, hãy gọi getData().
Chỉnh sửa thông tin về một người liên hệ ACTION_EDIT CONTENT_LOOKUP_URI cho người liên hệ. Hoạt động chỉnh sửa sẽ cho phép người dùng chỉnh sửa mọi dữ liệu liên kết với số liên hệ này. Contacts.CONTENT_ITEM_TYPE, một địa chỉ liên hệ duy nhất. Hiển thị màn hình Chỉnh sửa người liên hệ trong ứng dụng danh bạ. Các giá trị bổ sung mà bạn thêm vào ý định sẽ xuất hiện. Khi người dùng nhấp vào Xong để lưu nội dung chỉnh sửa, hoạt động của bạn sẽ quay lại nền trước.
Hiển thị một bộ chọn cũng có thể thêm dữ liệu. ACTION_INSERT_OR_EDIT Không áp dụng CONTENT_ITEM_TYPE Ý định này luôn hiển thị màn hình chọn của ứng dụng danh bạ. Người dùng có thể chọn một người liên hệ để chỉnh sửa hoặc thêm một người liên hệ mới. Màn hình chỉnh sửa hoặc màn hình thêm sẽ xuất hiện, tuỳ thuộc vào lựa chọn của người dùng và dữ liệu bổ sung mà bạn truyền trong ý định sẽ xuất hiện. Nếu ứng dụng của bạn hiển thị dữ liệu liên hệ, chẳng hạn như email hoặc số điện thoại, hãy sử dụng ý định này để cho phép người dùng thêm dữ liệu vào một người liên hệ hiện có. người liên hệ,

Lưu ý: Bạn không cần gửi giá trị tên trong các phần bổ sung của ý định này, vì người dùng luôn chọn một tên hiện có hoặc thêm một tên mới. Ngoài ra, nếu bạn gửi tên và người dùng chọn chỉnh sửa, thì ứng dụng danh bạ sẽ hiển thị tên mà bạn gửi, ghi đè giá trị trước đó. Nếu người dùng không nhận thấy điều này và lưu nội dung chỉnh sửa, thì giá trị cũ sẽ bị mất.

Ứng dụng danh bạ của thiết bị không cho phép bạn xoá một người liên hệ thô hoặc bất kỳ dữ liệu nào của người liên hệ đó bằng một ý định. Thay vào đó, để xoá một người liên hệ thô, hãy dùng ContentResolver.delete() hoặc ContentProviderOperation.newDelete().

Đoạn mã sau đây cho biết cách tạo và gửi một ý định chèn một số liên hệ và dữ liệu thô mới:

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

// Adds the row to the array
contactData.add(rawContactRow)

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
}

// Adds the row to the array
contactData.add(phoneRow)

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

    // Adds the email address and its type to the row
    put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
}

// Adds the row to the array
contactData.add(emailRow)

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

    // Sets the new contact name
    putExtra(ContactsContract.Intents.Insert.NAME, name)

    // Sets the new company and job title
    putExtra(ContactsContract.Intents.Insert.COMPANY, company)
    putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle)

    /*
    * Adds the array to the intent's extras. It must be a parcelable object in order to
    * travel between processes. The device's contacts app expects its key to be
    * Intents.Insert.DATA
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent)

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);

Tính toàn vẹn của dữ liệu

Vì kho lưu trữ danh bạ chứa dữ liệu quan trọng và nhạy cảm mà người dùng mong đợi là chính xác và mới nhất, nên Contacts Provider có các quy tắc được xác định rõ ràng về tính toàn vẹn của dữ liệu. Bạn có trách nhiệm tuân thủ các quy tắc này khi sửa đổi dữ liệu liên hệ. Các quy tắc quan trọng được liệt kê tại đây:

Luôn thêm một hàng ContactsContract.CommonDataKinds.StructuredName cho mọi hàng ContactsContract.RawContacts mà bạn thêm.
Một hàng ContactsContract.RawContacts không có hàng ContactsContract.CommonDataKinds.StructuredName trong bảng ContactsContract.Data có thể gây ra vấn đề trong quá trình tổng hợp.
Luôn liên kết các hàng ContactsContract.Data mới với hàng ContactsContract.RawContacts gốc.
Một hàng ContactsContract.Data không được liên kết với ContactsContract.RawContacts sẽ không xuất hiện trong ứng dụng danh bạ của thiết bị và có thể gây ra vấn đề với các bộ điều hợp đồng bộ hoá.
Chỉ thay đổi dữ liệu cho những người liên hệ thô mà bạn sở hữu.
Hãy nhớ rằng Trình cung cấp danh bạ thường quản lý dữ liệu từ nhiều loại tài khoản/dịch vụ trực tuyến khác nhau. Bạn cần đảm bảo rằng ứng dụng của bạn chỉ sửa đổi hoặc xoá dữ liệu cho những hàng thuộc về bạn, đồng thời chỉ chèn dữ liệu có loại tài khoản và tên mà bạn kiểm soát.
Luôn sử dụng các hằng số được xác định trong ContactsContract và các lớp con của hằng số này cho các cơ quan, URI nội dung, đường dẫn URI, tên cột, loại MIME và giá trị TYPE.
Việc sử dụng các hằng số này giúp bạn tránh được lỗi. Bạn cũng sẽ nhận được thông báo kèm theo cảnh báo của trình biên dịch nếu có hằng số nào không được dùng nữa.

Hàng dữ liệu tuỳ chỉnh

Bằng cách tạo và sử dụng các loại MIME tuỳ chỉnh của riêng mình, bạn có thể chèn, chỉnh sửa, xoá và truy xuất các hàng dữ liệu của riêng mình trong bảng ContactsContract.Data. Các hàng của bạn chỉ được sử dụng cột được xác định trong ContactsContract.DataColumns, mặc dù bạn có thể liên kết tên cột dành riêng cho loại của riêng mình với tên cột mặc định. Trong ứng dụng danh bạ của thiết bị, dữ liệu cho các hàng của bạn sẽ xuất hiện nhưng không thể chỉnh sửa hoặc xoá, đồng thời người dùng không thể thêm dữ liệu bổ sung. Để cho phép người dùng sửa đổi các hàng dữ liệu tuỳ chỉnh, bạn phải cung cấp một hoạt động trình chỉnh sửa trong ứng dụng của riêng mình.

Để hiển thị dữ liệu tuỳ chỉnh, hãy cung cấp một tệp contacts.xml chứa một phần tử <ContactsAccountType> và một hoặc nhiều phần tử con <ContactsDataKind> của phần tử đó. Điều này được mô tả chi tiết hơn trong phần <ContactsDataKind> element.

Để tìm hiểu thêm về các loại MIME tuỳ chỉnh, hãy đọc hướng dẫn Tạo trình cung cấp nội dung.

Bộ điều hợp đồng bộ hoá Trình cung cấp danh bạ

Contacts Provider được thiết kế đặc biệt để xử lý việc đồng bộ hoá dữ liệu danh bạ giữa một thiết bị và một dịch vụ trực tuyến. Việc này cho phép người dùng tải dữ liệu hiện có xuống một thiết bị mới và tải dữ liệu hiện có lên một tài khoản mới. Tính năng đồng bộ hoá cũng đảm bảo rằng người dùng có dữ liệu mới nhất trong tay, bất kể nguồn gốc của nội dung bổ sung và thay đổi. Một lợi ích khác của việc đồng bộ hoá là giúp dữ liệu liên hệ luôn có sẵn ngay cả khi thiết bị không kết nối với mạng.

Mặc dù bạn có thể triển khai quy trình đồng bộ hoá theo nhiều cách, nhưng hệ thống Android cung cấp một khung đồng bộ hoá trình bổ trợ giúp tự động hoá các tác vụ sau:

  • Đang kiểm tra phạm vi cung cấp mạng truyền hình.
  • Lập lịch và thực hiện đồng bộ hoá dựa trên lựa chọn ưu tiên của người dùng.
  • Khởi động lại các quá trình đồng bộ hoá đã dừng.

Để sử dụng khung này, bạn cung cấp một trình bổ trợ bộ điều hợp đồng bộ hoá. Mỗi trình điều hợp đồng bộ hoá là riêng biệt đối với một nhà cung cấp dịch vụ và nội dung, nhưng có thể xử lý nhiều tên tài khoản cho cùng một dịch vụ. Khung này cũng cho phép nhiều bộ điều hợp đồng bộ hoá cho cùng một dịch vụ và nhà cung cấp.

Các lớp và tệp của bộ điều hợp đồng bộ hoá

Bạn triển khai một bộ điều hợp đồng bộ hoá dưới dạng một lớp con của AbstractThreadedSyncAdapter và cài đặt bộ điều hợp đó trong ứng dụng Android. Hệ thống tìm hiểu về trình điều hợp đồng bộ hoá từ các phần tử trong tệp kê khai ứng dụng và từ một tệp XML đặc biệt mà tệp kê khai trỏ đến. Tệp XML xác định loại tài khoản cho dịch vụ trực tuyến và quyền của nhà cung cấp nội dung. Hai yếu tố này cùng nhau xác định một cách duy nhất bộ chuyển đổi. Trình điều hợp đồng bộ hoá sẽ không hoạt động cho đến khi người dùng thêm một tài khoản cho loại tài khoản của trình điều hợp đồng bộ hoá và bật tính năng đồng bộ hoá cho trình cung cấp nội dung mà trình điều hợp đồng bộ hoá đồng bộ hoá. Khi đó, hệ thống sẽ bắt đầu quản lý bộ chuyển đổi, gọi bộ chuyển đổi khi cần thiết để đồng bộ hoá giữa trình cung cấp nội dung và máy chủ.

Lưu ý: Việc sử dụng một loại tài khoản trong quá trình nhận dạng của bộ điều hợp đồng bộ hoá cho phép hệ thống phát hiện và nhóm các bộ điều hợp đồng bộ hoá truy cập vào các dịch vụ khác nhau của cùng một tổ chức. Ví dụ: tất cả các bộ điều hợp đồng bộ hoá cho các dịch vụ trực tuyến của Google đều có cùng một loại tài khoản com.google. Khi người dùng thêm Tài khoản Google vào thiết bị, tất cả các trình điều hợp đồng bộ hoá đã cài đặt cho các dịch vụ của Google sẽ được liệt kê cùng nhau; mỗi trình điều hợp đồng bộ hoá được liệt kê sẽ đồng bộ hoá với một trình cung cấp nội dung khác trên thiết bị.

Vì hầu hết các dịch vụ đều yêu cầu người dùng xác minh danh tính trước khi truy cập vào dữ liệu, nên hệ thống Android cung cấp một khung xác thực tương tự như khung bộ điều hợp đồng bộ hoá và thường được dùng cùng với khung này. Khung xác thực sử dụng các trình xác thực bổ trợ là các lớp con của AbstractAccountAuthenticator. Trình xác thực sẽ xác minh danh tính của người dùng theo các bước sau:

  1. Thu thập tên, mật khẩu hoặc thông tin tương tự của người dùng (thông tin đăng nhập của người dùng).
  2. Gửi thông tin xác thực đến dịch vụ
  3. Kiểm tra câu trả lời của dịch vụ.

Nếu dịch vụ chấp nhận thông tin xác thực, thì trình xác thực có thể lưu trữ thông tin xác thực để sử dụng sau này. Do khung xác thực trình bổ trợ, AccountManager có thể cung cấp quyền truy cập vào mọi mã xác thực mà trình xác thực hỗ trợ và chọn hiển thị, chẳng hạn như mã xác thực OAuth2.

Mặc dù không bắt buộc phải xác thực, nhưng hầu hết các dịch vụ danh bạ đều sử dụng phương thức này. Tuy nhiên, bạn không bắt buộc phải sử dụng khung xác thực Android để xác thực.

Triển khai bộ điều hợp đồng bộ hoá

Để triển khai một bộ điều hợp đồng bộ hoá cho Trình cung cấp danh bạ, bạn bắt đầu bằng cách tạo một ứng dụng Android chứa những thành phần sau:

Một thành phần Service phản hồi các yêu cầu từ hệ thống để liên kết với bộ điều hợp đồng bộ hoá.
Khi muốn chạy một quy trình đồng bộ hoá, hệ thống sẽ gọi phương thức onBind() của dịch vụ để nhận một IBinder cho bộ điều hợp đồng bộ hoá. Điều này cho phép hệ thống thực hiện các lệnh gọi trên nhiều quy trình đến các phương thức của bộ chuyển đổi.
Bộ điều hợp đồng bộ hoá thực tế, được triển khai dưới dạng một lớp con cụ thể của AbstractThreadedSyncAdapter.
Lớp này thực hiện việc tải dữ liệu xuống từ máy chủ, tải dữ liệu lên từ thiết bị và giải quyết xung đột. Công việc chính của bộ chuyển đổi được thực hiện trong phương thức onPerformSync(). Lớp này phải được khởi tạo dưới dạng một singleton.
Một lớp con của Application.
Lớp này đóng vai trò là một nhà máy cho singleton bộ điều hợp đồng bộ hoá. Sử dụng phương thức onCreate() để tạo thực thể bộ điều hợp đồng bộ hoá và cung cấp một phương thức "getter" tĩnh để trả về singleton cho phương thức onBind() của dịch vụ bộ điều hợp đồng bộ hoá.
Không bắt buộc: Một thành phần Service phản hồi các yêu cầu từ hệ thống để xác thực người dùng.
AccountManager khởi động dịch vụ này để bắt đầu quy trình xác thực. Phương thức onCreate() của dịch vụ sẽ tạo một đối tượng trình xác thực. Khi hệ thống muốn xác thực tài khoản người dùng cho trình kết nối đồng bộ hoá của ứng dụng, hệ thống sẽ gọi phương thức onBind() của dịch vụ để nhận IBinder cho trình xác thực. Điều này cho phép hệ thống thực hiện các lệnh gọi trên nhiều quy trình đến các phương thức của trình xác thực.
Không bắt buộc: Một lớp con cụ thể của AbstractAccountAuthenticator xử lý các yêu cầu xác thực.
Lớp này cung cấp các phương thức mà AccountManager gọi để xác thực thông tin đăng nhập của người dùng với máy chủ. Thông tin chi tiết về quy trình xác thực rất đa dạng, tuỳ thuộc vào công nghệ máy chủ đang được sử dụng. Bạn nên tham khảo tài liệu về phần mềm máy chủ để tìm hiểu thêm về quy trình xác thực.
Các tệp XML xác định trình đồng bộ hoá và trình xác thực cho hệ thống.
Các thành phần dịch vụ trình xác thực và bộ điều hợp đồng bộ hoá được mô tả trước đó được xác định trong các phần tử <service> trong tệp kê khai ứng dụng. Các phần tử này chứa các phần tử con <meta-data> cung cấp dữ liệu cụ thể cho hệ thống:
  • Phần tử <meta-data> cho dịch vụ bộ điều hợp đồng bộ hoá trỏ đến tệp XML res/xml/syncadapter.xml. Đến lượt, tệp này chỉ định một URI cho dịch vụ web sẽ được đồng bộ hoá với Trình cung cấp danh bạ và một loại tài khoản cho dịch vụ web.
  • Không bắt buộc: Phần tử <meta-data> cho trình xác thực trỏ đến tệp XML res/xml/authenticator.xml. Đến lượt, tệp này sẽ chỉ định loại tài khoản mà trình xác thực này hỗ trợ, cũng như các tài nguyên giao diện người dùng xuất hiện trong quá trình xác thực. Loại tài khoản được chỉ định trong phần tử này phải giống với loại tài khoản được chỉ định cho bộ điều hợp đồng bộ hoá.

Dữ liệu luồng mạng xã hội

Các bảng android.provider.ContactsContract.StreamItems và android.provider.ContactsContract.StreamItemPhotos quản lý dữ liệu đến từ các mạng xã hội. Bạn có thể viết một bộ điều hợp đồng bộ hoá để thêm dữ liệu luồng từ mạng của riêng bạn vào các bảng này, hoặc bạn có thể đọc dữ liệu luồng từ các bảng này và hiển thị dữ liệu đó trong ứng dụng của riêng bạn, hoặc cả hai. Với những tính năng này, các dịch vụ và ứng dụng mạng xã hội của bạn có thể được tích hợp vào trải nghiệm mạng xã hội của Android.

Văn bản trong luồng nội dung trên mạng xã hội

Các mục trong bảng tin luôn được liên kết với một người liên hệ thô. android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID liên kết đến giá trị _ID cho danh bạ thô. Loại tài khoản và tên tài khoản của người liên hệ thô cũng được lưu trữ trong hàng mục luồng.

Lưu trữ dữ liệu từ luồng của bạn trong các cột sau:

android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
Bắt buộc. Loại tài khoản của người dùng cho danh bạ thô được liên kết với mục này trong bảng tin. Đừng quên đặt giá trị này khi bạn chèn một mục trong luồng.
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
Bắt buộc. Tên tài khoản của người dùng cho danh bạ thô được liên kết với mục này trong bảng tin. Đừng quên đặt giá trị này khi bạn chèn một mục trong luồng.
Cột giá trị nhận dạng
Bắt buộc. Bạn phải chèn các cột giá trị nhận dạng sau đây khi chèn một mục trong luồng phát:
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: Giá trị android.provider.BaseColumns#_ID của người liên hệ mà mục trong bảng tin này được liên kết.
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: Giá trị android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY của người liên hệ mà mục luồng này được liên kết.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Giá trị android.provider.BaseColumns#_ID của số điện thoại liên hệ thô mà mục trong bảng tin này được liên kết.
android.provider.ContactsContract.StreamItemsColumns#COMMENTS
Không bắt buộc. Lưu trữ thông tin tóm tắt mà bạn có thể hiển thị ở đầu một mục trong luồng.
android.provider.ContactsContract.StreamItemsColumns#TEXT
Văn bản của mục trong luồng, có thể là nội dung do nguồn của mục đăng hoặc nội dung mô tả về một hành động nào đó đã tạo ra mục trong luồng. Cột này có thể chứa mọi định dạng và hình ảnh tài nguyên được nhúng mà fromHtml() có thể hiển thị. Nhà cung cấp có thể cắt ngắn hoặc lược bớt nội dung dài, nhưng sẽ cố gắng tránh làm hỏng thẻ.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
Một chuỗi văn bản chứa thời gian mục trong luồng được chèn hoặc cập nhật, dưới dạng mili giây kể từ thời gian bắt đầu của hệ thống. Các ứng dụng chèn hoặc cập nhật các mục trong luồng chịu trách nhiệm duy trì cột này; Nhà cung cấp danh bạ không tự động duy trì cột này.

Để hiển thị thông tin nhận dạng cho các mục trong luồng, hãy dùng android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL và android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE để liên kết với các tài nguyên trong ứng dụng của bạn.

Bảng android.provider.ContactsContract.StreamItems cũng chứa các cột android.provider.ContactsContract.StreamItemsColumns#SYNC1 thông qua android.provider.ContactsContract.StreamItemsColumns#SYNC4 để chỉ bộ chuyển đổi đồng bộ hoá sử dụng.

Ảnh trên mạng xã hội

Bảng android.provider.ContactsContract.StreamItemPhotos lưu trữ những bức ảnh được liên kết với một mục trong bảng tin. Cột android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID của bảng liên kết đến các giá trị trong cột _ID của bảng android.provider.ContactsContract.StreamItems. Thông tin tham chiếu về ảnh được lưu trữ trong bảng trong các cột sau:

Cột android.provider.ContactsContract.StreamItemPhotos#PHOTO (một BLOB).
Một biểu diễn nhị phân của bức ảnh, được nhà cung cấp đổi kích thước để lưu trữ và hiển thị. Cột này được cung cấp để tương thích ngược với các phiên bản trước của Trình cung cấp danh bạ đã dùng cột này để lưu trữ ảnh. Tuy nhiên, trong phiên bản hiện tại, bạn không nên dùng cột này để lưu trữ ảnh. Thay vào đó, hãy dùng android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID hoặc android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (cả hai đều được mô tả trong các điểm sau) để lưu trữ ảnh trong một tệp. Cột này hiện chứa hình thu nhỏ của bức ảnh mà bạn có thể đọc.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Mã nhận dạng bằng số của ảnh cho một người liên hệ thô. Nối giá trị này vào hằng số DisplayPhoto.CONTENT_URI để lấy một URI nội dung trỏ đến một tệp ảnh duy nhất, rồi gọi openAssetFileDescriptor() để lấy một giá trị nhận dạng cho tệp ảnh.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
Một URI nội dung trỏ trực tiếp đến tệp ảnh cho bức ảnh do hàng này đại diện. Gọi openAssetFileDescriptor() bằng URI này để lấy một tệp ảnh.

Sử dụng bảng luồng hoạt động trên mạng xã hội

Các bảng này hoạt động giống như các bảng chính khác trong Trình cung cấp danh bạ, ngoại trừ:

  • Các bảng này cần có thêm quyền truy cập. Để đọc dữ liệu từ các nguồn này, ứng dụng của bạn phải có quyền android.Manifest.permission#READ_SOCIAL_STREAM. Để sửa đổi các thông tin này, ứng dụng của bạn phải có quyền android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • Đối với bảng android.provider.ContactsContract.StreamItems, số lượng hàng được lưu trữ cho mỗi người liên hệ thô bị giới hạn. Khi đạt đến giới hạn này, Trình cung cấp danh bạ sẽ tạo không gian cho các hàng mục luồng mới bằng cách tự động xoá các hàng có android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP cũ nhất. Để lấy hạn mức, hãy đưa ra một truy vấn cho URI nội dung android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Bạn có thể để tất cả các đối số khác ngoài URI nội dung được đặt thành null. Truy vấn này trả về một Con trỏ chứa một hàng duy nhất, với cột duy nhất android.provider.ContactsContract.StreamItems#MAX_ITEMS.

Lớp android.provider.ContactsContract.StreamItems.StreamItemPhotos xác định một bảng phụ của android.provider.ContactsContract.StreamItemPhotos chứa các hàng ảnh cho một mục trong luồng duy nhất.

Tương tác trên luồng mạng xã hội

Dữ liệu luồng hoạt động trên mạng xã hội do Trình cung cấp danh bạ quản lý, kết hợp với ứng dụng danh bạ của thiết bị, mang đến một cách hiệu quả để kết nối hệ thống mạng xã hội của bạn với danh bạ hiện có. Các tính năng sau đây hiện có:

  • Bằng cách đồng bộ hoá dịch vụ mạng xã hội với Trình cung cấp danh bạ bằng một bộ điều hợp đồng bộ hoá, bạn có thể truy xuất hoạt động gần đây cho danh bạ của người dùng và lưu trữ hoạt động đó trong các bảng android.provider.ContactsContract.StreamItems và android.provider.ContactsContract.StreamItemPhotos để sử dụng sau này.
  • Ngoài việc đồng bộ hoá thường xuyên, bạn có thể kích hoạt bộ điều hợp đồng bộ hoá để truy xuất dữ liệu bổ sung khi người dùng chọn một số liên hệ để xem. Điều này cho phép trình điều hợp đồng bộ hoá của bạn truy xuất ảnh có độ phân giải cao và các mục gần đây nhất trong luồng cho người liên hệ.
  • Bằng cách đăng ký một thông báo với ứng dụng danh bạ của thiết bị và Contacts Provider, bạn có thể nhận một ý định khi một người liên hệ được xem và tại thời điểm đó, hãy cập nhật trạng thái của người liên hệ từ dịch vụ của bạn. Phương pháp này có thể nhanh hơn và sử dụng ít băng thông hơn so với việc đồng bộ hoá toàn bộ bằng bộ điều hợp đồng bộ hoá.
  • Người dùng có thể thêm một người liên hệ vào dịch vụ mạng xã hội của bạn trong khi xem người liên hệ đó trong ứng dụng danh bạ của thiết bị. Bạn có thể bật tính năng này bằng tính năng "mời người liên hệ". Bạn có thể bật tính năng này bằng cách kết hợp một hoạt động thêm người liên hệ hiện có vào mạng của bạn và một tệp XML cung cấp ứng dụng danh bạ của thiết bị và Trình cung cấp danh bạ với thông tin chi tiết về ứng dụng của bạn.

Việc đồng bộ hoá thường xuyên các mục trong luồng với Trình cung cấp danh bạ cũng giống như các hoạt động đồng bộ hoá khác. Để tìm hiểu thêm về việc đồng bộ hoá, hãy xem phần Bộ điều hợp đồng bộ hoá Trình cung cấp danh bạ. Việc đăng ký thông báo và mời người liên hệ sẽ được đề cập trong hai phần tiếp theo.

Đăng ký để xử lý các khung hiển thị mạng xã hội

Để đăng ký bộ điều hợp đồng bộ hoá nhằm nhận thông báo khi người dùng xem một số liên hệ do bộ điều hợp đồng bộ hoá của bạn quản lý:

  1. Tạo một tệp có tên là contacts.xml trong thư mục res/xml/ của dự án. Nếu đã có tệp này, bạn có thể bỏ qua bước này.
  2. Trong tệp này, hãy thêm phần tử <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này.
  3. Để đăng ký một dịch vụ được thông báo khi người dùng mở trang chi tiết của một người liên hệ trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewContactNotifyService="serviceclass" vào phần tử, trong đó serviceclass là tên lớp đủ điều kiện của dịch vụ sẽ nhận ý định từ ứng dụng danh bạ của thiết bị. Đối với dịch vụ thông báo, hãy dùng một lớp mở rộng IntentService để cho phép dịch vụ nhận các ý định. Dữ liệu trong ý định đến chứa URI nội dung của liên hệ thô mà người dùng đã nhấp vào. Từ dịch vụ thông báo, bạn có thể liên kết rồi gọi trình điều hợp đồng bộ hoá để cập nhật dữ liệu cho số liên hệ thô.

Cách đăng ký một hoạt động sẽ được gọi khi người dùng nhấp vào một mục trong luồng, ảnh hoặc cả hai:

  1. Tạo một tệp có tên là contacts.xml trong thư mục res/xml/ của dự án. Nếu đã có tệp này, bạn có thể bỏ qua bước này.
  2. Trong tệp này, hãy thêm phần tử <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này.
  3. Để đăng ký một trong các hoạt động của bạn nhằm xử lý việc người dùng nhấp vào một mục trong luồng trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewStreamItemActivity="activityclass" vào phần tử, trong đó activityclass là tên lớp đủ điều kiện của hoạt động sẽ nhận ý định từ ứng dụng danh bạ của thiết bị.
  4. Để đăng ký một trong các hoạt động của bạn để xử lý việc người dùng nhấp vào ảnh trong luồng trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewStreamItemPhotoActivity="activityclass" vào phần tử, trong đó activityclass là tên lớp đủ điều kiện của hoạt động sẽ nhận ý định từ ứng dụng danh bạ của thiết bị.

Phần tử <ContactsAccountType> được mô tả chi tiết hơn trong phần phần tử<ContactsAccountType>.

Ý định đến chứa URI nội dung của mục hoặc ảnh mà người dùng đã nhấp vào. Để có các hoạt động riêng biệt cho các mục văn bản và ảnh, hãy sử dụng cả hai thuộc tính trong cùng một tệp.

Tương tác với dịch vụ mạng xã hội

Người dùng không cần rời khỏi ứng dụng danh bạ của thiết bị để mời một người liên hệ tham gia trang mạng xã hội của bạn. Thay vào đó, bạn có thể yêu cầu ứng dụng danh bạ của thiết bị gửi một ý định mời người liên hệ tham gia một trong các hoạt động của bạn. Cách thiết lập chế độ này:

  1. Tạo một tệp có tên là contacts.xml trong thư mục res/xml/ của dự án. Nếu đã có tệp này, bạn có thể bỏ qua bước này.
  2. Trong tệp này, hãy thêm phần tử <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này.
  3. Thêm các thuộc tính sau:
    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"
    Giá trị activityclass là tên lớp đủ điều kiện của hoạt động sẽ nhận ý định. Giá trị invite_action_label là một chuỗi văn bản xuất hiện trong trình đơn Thêm kết nối trong ứng dụng danh bạ của thiết bị.

Lưu ý: ContactsSource là tên thẻ không còn dùng nữa cho ContactsAccountType.

Tài liệu tham khảo contacts.xml

Tệp contacts.xml chứa các phần tử XML kiểm soát hoạt động tương tác của bộ điều hợp đồng bộ hoá và ứng dụng với ứng dụng danh bạ và Trình cung cấp danh bạ. Những phần tử này được mô tả trong các phần sau.

Phần tử <ContactsAccountType>

Phần tử <ContactsAccountType> kiểm soát hoạt động tương tác của ứng dụng với ứng dụng danh bạ. Cú pháp của hàm này như sau:

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">

có trong:

res/xml/contacts.xml

có thể chứa:

<ContactsDataKind>

Description:

Khai báo các thành phần Android và nhãn giao diện người dùng cho phép người dùng mời một trong số những người liên hệ của họ tham gia mạng xã hội, thông báo cho người dùng khi một trong các luồng mạng xã hội của họ được cập nhật, v.v.

Xin lưu ý rằng bạn không cần tiền tố thuộc tính android: cho các thuộc tính của <ContactsAccountType>.

Thuộc tính:

inviteContactActivity
Tên lớp đủ điều kiện của hoạt động trong ứng dụng mà bạn muốn kích hoạt khi người dùng chọn Thêm kết nối trong ứng dụng danh bạ của thiết bị.
inviteContactActionLabel
Một chuỗi văn bản xuất hiện cho hoạt động được chỉ định trong inviteContactActivity, trong trình đơn Thêm đường kết nối. Ví dụ: bạn có thể sử dụng chuỗi "Theo dõi trong mạng của tôi". Bạn có thể sử dụng giá trị nhận dạng tài nguyên chuỗi cho nhãn này.
viewContactNotifyService
Tên lớp đủ điều kiện của một dịch vụ trong ứng dụng của bạn sẽ nhận được thông báo khi người dùng xem một số liên hệ. Thông báo này do ứng dụng danh bạ của thiết bị gửi; thông báo này cho phép ứng dụng của bạn hoãn các thao tác sử dụng nhiều dữ liệu cho đến khi cần. Ví dụ: ứng dụng của bạn có thể phản hồi thông báo này bằng cách đọc và hiển thị ảnh có độ phân giải cao của người liên hệ cũng như các mục gần đây nhất trong luồng nội dung trên mạng xã hội. Tính năng này được mô tả chi tiết hơn trong phần Tương tác trên luồng mạng xã hội.
viewGroupActivity
Tên lớp đủ điều kiện của một hoạt động trong ứng dụng có thể hiển thị thông tin nhóm. Khi người dùng nhấp vào nhãn nhóm trong ứng dụng danh bạ của thiết bị, giao diện người dùng cho hoạt động này sẽ xuất hiện.
viewGroupActionLabel
Nhãn mà ứng dụng danh bạ hiển thị cho một chế độ kiểm soát giao diện người dùng cho phép người dùng xem các nhóm trong ứng dụng của bạn.

Giá trị nhận dạng tài nguyên chuỗi được phép dùng cho thuộc tính này.

viewStreamItemActivity
Tên lớp đủ điều kiện của một hoạt động trong ứng dụng mà ứng dụng danh bạ của thiết bị sẽ chạy khi người dùng nhấp vào một mục trong luồng cho một số liên hệ thô.
viewStreamItemPhotoActivity
Tên lớp đủ điều kiện của một hoạt động trong ứng dụng mà ứng dụng danh bạ của thiết bị sẽ chạy khi người dùng nhấp vào một bức ảnh trong mục luồng cho một số liên hệ thô.

Phần tử <ContactsDataKind>

Phần tử <ContactsDataKind> kiểm soát việc hiển thị các hàng dữ liệu tuỳ chỉnh của ứng dụng trong giao diện người dùng của ứng dụng danh bạ. Cú pháp của hàm này như sau:

<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">

có trong:

<ContactsAccountType>

Description:

Sử dụng phần tử này để ứng dụng danh bạ hiển thị nội dung của một hàng dữ liệu tuỳ chỉnh trong phần thông tin chi tiết của một người liên hệ thô. Mỗi phần tử con <ContactsDataKind> của <ContactsAccountType> đại diện cho một loại hàng dữ liệu tuỳ chỉnh mà trình điều hợp đồng bộ hoá của bạn thêm vào bảng ContactsContract.Data. Thêm một phần tử <ContactsDataKind> cho mỗi loại MIME tuỳ chỉnh mà bạn sử dụng. Bạn không cần thêm phần tử này nếu có một hàng dữ liệu tuỳ chỉnh mà bạn không muốn hiển thị dữ liệu.

Thuộc tính:

android:mimeType
Loại MIME tuỳ chỉnh mà bạn đã xác định cho một trong các loại hàng dữ liệu tuỳ chỉnh trong bảng ContactsContract.Data. Ví dụ: giá trị vnd.android.cursor.item/vnd.example.locationstatus có thể là một loại MIME tuỳ chỉnh cho một hàng dữ liệu ghi lại vị trí xác định gần đây nhất của một người liên hệ.
android:icon
Một tài nguyên có thể vẽ trên Android mà ứng dụng danh bạ hiển thị bên cạnh dữ liệu của bạn. Sử dụng thuộc tính này để cho người dùng biết rằng dữ liệu đến từ dịch vụ của bạn.
android:summaryColumn
Tên cột cho giá trị đầu tiên trong số 2 giá trị được truy xuất từ hàng dữ liệu. Giá trị này xuất hiện dưới dạng dòng đầu tiên của mục nhập cho hàng dữ liệu này. Dòng đầu tiên được dùng làm bản tóm tắt dữ liệu, nhưng đây là thông tin không bắt buộc. Xem thêm android:detailColumn.
android:detailColumn
Tên cột cho giá trị thứ hai trong hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này xuất hiện dưới dạng dòng thứ hai của mục nhập cho hàng dữ liệu này. Xem thêm android:summaryColumn.

Các tính năng bổ sung của Trình cung cấp danh bạ

Ngoài các tính năng chính được mô tả trong các phần trước, Trình cung cấp danh bạ còn cung cấp những tính năng hữu ích sau đây để xử lý dữ liệu danh bạ:

  • Nhóm người liên hệ
  • Tính năng cho ảnh

Nhóm người liên hệ

Trình cung cấp danh bạ có thể tuỳ ý gắn nhãn cho các tập hợp người liên hệ có liên quan bằng dữ liệu group. Nếu máy chủ được liên kết với một tài khoản người dùng muốn duy trì các nhóm, thì trình điều hợp đồng bộ hoá cho loại tài khoản của tài khoản đó sẽ chuyển dữ liệu nhóm giữa Trình cung cấp danh bạ và máy chủ. Khi người dùng thêm một người liên hệ mới vào máy chủ rồi thêm người liên hệ này vào một nhóm mới, bộ điều hợp đồng bộ hoá phải thêm nhóm mới vào bảng ContactsContract.Groups. (Các) nhóm mà một người liên hệ thô thuộc về được lưu trữ trong bảng ContactsContract.Data bằng cách sử dụng loại MIME ContactsContract.CommonDataKinds.GroupMembership.

Nếu đang thiết kế một bộ chuyển đổi đồng bộ hoá sẽ thêm dữ liệu liên hệ thô từ máy chủ vào Trình cung cấp danh bạ và bạn không sử dụng các nhóm, thì bạn cần yêu cầu Trình cung cấp hiển thị dữ liệu của bạn. Trong mã được thực thi khi người dùng thêm tài khoản vào thiết bị, hãy cập nhật hàng ContactsContract.Settings mà Trình cung cấp danh bạ thêm cho tài khoản. Trong hàng này, hãy đặt giá trị của cột Settings.UNGROUPED_VISIBLE thành 1. Khi bạn làm như vậy, Trình cung cấp danh bạ sẽ luôn hiển thị dữ liệu danh bạ của bạn, ngay cả khi bạn không sử dụng nhóm.

Ảnh của người liên hệ

Bảng ContactsContract.Data lưu trữ ảnh dưới dạng các hàng có loại MIME Photo.CONTENT_ITEM_TYPE. Cột CONTACT_ID của hàng được liên kết với cột _ID của danh bạ thô mà hàng đó thuộc về. Lớp ContactsContract.Contacts.Photo xác định một bảng phụ của ContactsContract.Contacts chứa thông tin về ảnh cho ảnh chính của một người liên hệ, đây là ảnh chính của người liên hệ chính. Tương tự, lớp ContactsContract.RawContacts.DisplayPhoto xác định một bảng phụ của ContactsContract.RawContacts chứa thông tin ảnh cho ảnh chính của một số liên hệ thô.

Tài liệu tham khảo cho ContactsContract.Contacts.PhotoContactsContract.RawContacts.DisplayPhoto chứa các ví dụ về cách truy xuất thông tin ảnh. Không có lớp tiện ích nào để truy xuất hình thu nhỏ chính cho một số liên hệ thô, nhưng bạn có thể gửi một truy vấn đến bảng ContactsContract.Data, chọn _ID, Photo.CONTENT_ITEM_TYPE và cột IS_PRIMARY của số liên hệ thô để tìm hàng ảnh chính của số liên hệ thô.

Dữ liệu luồng mạng xã hội của một người cũng có thể bao gồm ảnh. Các thông tin này được lưu trữ trong bảng android.provider.ContactsContract.StreamItemPhotos. Bảng này được mô tả chi tiết hơn trong phần Ảnh trong luồng nội dung trên mạng xã hội.