Trình cung cấp danh bạ

Trình cung cấp danh bạ là một thành phần Android mạnh mẽ và linh hoạt, giúp quản lý kho lưu trữ trung tâm của thiết bị về dữ liệu về con người. Nhà cung cấp danh bạ 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 trở nên phức tạp. Do đó, API của nhà cung cấp bao gồm một tập hợp các lớp và giao diện hợp đồng rộng rãi, hỗ trợ 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 trình cung cấp cơ bản.
  • Cách truy xuất dữ liệu từ nhà cung cấp.
  • Cách sửa đổi dữ liệu trong trình cung cấp.
  • Cách ghi bộ điều hợp đồng bộ hoá để đồng bộ hoá dữ liệu từ máy chủ của bạn vào Trình cung cấp danh bạ.

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

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

Trình cung cấp danh bạ là một thành phần của trình cung cấp nội dung Android. Cấu phần này 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 theo tên của các lớp hợp đồng. Các lớp 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:

Bảng ContactsContract.Contacts
Các hàng đại diện cho nhiều người, dựa trên dữ liệu tổng hợp của các hàng liên hệ thô.
Bảng ContactsContract.RawContacts
Các hàng chứa thông tin tóm tắt về dữ liệu của một người, dành riêng cho một tài khoản người dùng và loại tài khoản.
Bảng ContactsContract.Data
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 trong ContactsContract đại diện là các bảng phụ trợ mà Nhà cung cấp danh bạ sử dụng để quản lý hoạt động hoặc hỗ trợ các chức năng cụ thể trong ứng dụng điện thoại hoặc danh bạ của thiết bị.

Danh bạ thô

Thông tin 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 có nhiều mục liên hệ thô cho cùng một người. Nhiều địa chỉ liên hệ 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 của một người liên hệ thô không được lưu trữ trong bảng ContactsContract.RawContacts. Thay vào đó, dữ liệu 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 mẹ ContactsContract.RawContacts.

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ú tiếp theo sau bảng:

Bảng 1. Các cột quan trọng của người liên hệ thô.

Tên cột Mục đích sử dụng Ghi chú
ACCOUNT_NAME Tên tài khoản của loại tài khoản là nguồn của thông tin liên hệ thô này. Ví dụ: tên tài khoản của một Tài khoản Google là một trong các địa chỉ Gmail của chủ sở hữu thiết bị. Hãy xem mục tiếp theo về ACCOUNT_TYPE để biết thêm thông tin. Định dạng của tên này là riêng cho loại tài khoản. 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 thông tin liên hệ thô này. Ví dụ: loại tài khoản của Tài khoản Google là com.google. Luôn xác định loại tài khoản bằng 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. Một loại tài khoản cung cấp dữ liệu về danh bạ thường có một bộ điều hợp đồng bộ hoá liên kết 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 trình chuyển đổi đồng bộ hoá có thể xoá hàng khỏi máy chủ của chúng, sau đó xoá hàng khỏi kho lưu trữ.

Ghi chú

Sau đây là các ghi chú 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 liên hệ đó trong ContactsContract.RawContacts. Thay vào đó, dữ liệu 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 chính bạn trong hàng thông tin liên hệ thô, trước tiên, dữ liệu này phải được đăng ký với 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ô.

    Ví dụ: nếu bạn muốn ứng dụng duy trì dữ liệu liên hệ cho dịch vụ dựa trên web của mình bằng miền com.example.dataservice và tài khoản của người dùng cho dịch vụ 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) trước khi ứng dụng của bạn 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 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 liên hệ 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ó ba tài khoản người dùng sau đây được xác định trên thiết bị của cô:

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

Người dùng này đã bật tuỳ chọn Sync Contacts (Đồng bộ hoá danh bạ) cho cả 3 tài khoản này trong phần cài đặt Accounts (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ạ và thêm "Thomas Higginson". Sau đó, cô đăng nhập vào Gmail với tư cách là emilyd@gmail.com và gửi email đến "Thomas Higginson". Thao tác này sẽ tự động thêm anh vào danh bạ. 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 liên hệ thô sau khi thực hiện công việc này:

  1. Một 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. Liên hệ thô thứ hai của "Thomas Higginson" có liên quan đến emilyd@gmail.com. Loại tài khoản người dùng cũng là Google. Có một người 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. Một thông tin 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 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 liên hệ thô. Điều này cho phép một người liên hệ thô duy nhất có nhiều bản sao 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 liên kết với tài khoản Google emilyd@gmail.com) có địa chỉ email gia đình là thigg@gmail.com và địa chỉ email công việc là thomas.higginson@gmail.com, thì Nhà cung cấp danh bạ sẽ lưu trữ hai hàng địa chỉ email và liên kết cả hai hàng đó với liên hệ thô.

Lưu ý rằng nhiều loại dữ liệu được lưu trữ trong một bảng duy nhất này. Các hàng chi tiết về tên hiển thị, số điện thoại, email, địa chỉ bưu chính, ảnh và trang web đều có 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ả

Một số ví dụ về tên cột mang tính mô tả:

RAW_CONTACT_ID
Giá trị của cột _ID của người liên hệ 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 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à nguồn mở và bất kỳ ứng dụng hoặc bộ chuyển đổi đồng bộ hoá nào hoạt động với Nhà cung cấp danh bạ đều có thể sử dụng.
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ữ 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 giá trị khác 0.

Tên cột chung chung

Có 15 cột chung có tên từ DATA1 đến DATA15 thường được cung cấp và 4 cột chung khác từ SYNC1 đến SYNC4 mà chỉ bộ chuyển đổi đồng bộ hoá mới dùng được. Các hằng 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. Nhà cung cấp danh bạ luôn sử dụng cột này cho dữ liệu mà nhà cung cấp dự kiến sẽ là mục tiêu thường xuyên nhất của truy vấn. Ví dụ: trong 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 lớn nhị phân (BLOB), chẳng hạn như hình thu nhỏ ảnh.

Tên cột theo loại

Để tạo điều kiện làm việc với các cột cho một loại hàng cụ thể, Trình cung cấp thông tin liên hệ cũng cung cấp các hằng số tên cột theo loại, được xác định trong các lớp con của ContactsContract.CommonDataKinds. 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. Nhờ vậy, bạn có thể truy cập vào dữ liệu trong một hàng thuộc một kiểu cụ thể.

Ví dụ: lớp ContactsContract.CommonDataKinds.Email xác định hằng số tên cột theo kiểu cụ thể cho hàng ContactsContract.Data có kiểu 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 nhà cung cấp. Nếu làm như vậy, bạn có thể mất dữ liệu hoặc khiến nhà cung cấp gặp sự cố. Ví dụ: bạn không nên thêm một hàng có loại MIME là 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ể xác định tên cột cho từng 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 hàng ContactsContract.Data, cũng như cách tên cột theo loại "lớp phủ" tên cột chung

Cách tên cột theo loại liên kết 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 theo từng 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 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.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. Địa chỉ 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 cho người liên hệ thô liên kết với hàng dữ liệu này. Một địa chỉ liên hệ 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 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. Họ đượ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 người liên hệ. Điều này tạo điều kiện cho việc 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 hàng địa chỉ liên hệ mới và tổng hợp các địa chỉ liên hệ thô với một hàng địa chỉ liên hệ hiện có. Cả ứng dụng và bộ điều hợp đồng bộ hoá đều không được phép thêm danh bạ và một số cột trong hàng thông tin liên hệ ở chế độ 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 trường hợp 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ể đọ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ệ nào hiện có. Nhà cung cấp cũng thực hiện việc này nếu dữ liệu thô của một người liên hệ hiện có thay đổi theo cách không còn khớp với người liên hệ mà dữ liệu đó được đính kèm trước đó. Nếu một ứng dụng hoặc trình chuyển đổi đồng bộ hoá tạo một người liên hệ thô mới 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 thông tin liên hệ với các hàng thông tin liên hệ thô của hàng đó với cột _ID của hàng thông tin liên hệ 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 từng hàng danh bạ thô.

Bảng ContactsContract.Contacts cũng có cột LOOKUP_KEY là một đườ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 cung cấp này có thể thay đổi giá trị _ID của hàng danh bạ để phản hồi hoạt động tổng hợp hoặc đồng bộ hoá. Ngay cả trong trường hợp này, URI nội dung CONTENT_LOOKUP_URI kết hợp với LOOKUP_KEY của địa chỉ liên hệ vẫn sẽ trỏ đến hàng địa chỉ liên hệ. Vì vậy, bạn có thể sử dụng LOOKUP_KEY để duy trì đường liên kết đến các địa chỉ 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 ba bảng chính.

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

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

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

Trong các điều kiện được đề 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 ở 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ề danh bạ thường xuyên. Xin lưu ý rằng một số trường trong số này chỉ ảnh hưởng đến thứ hạng của danh bạ khi danh bạ 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ể thực hiện một số trường hợp sử dụng nhất định bằng cách sử 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 thay đổi này không ảnh hưởng đến chức năng của ứng dụng, bạn có thể xoá những trường dữ liệu này theo cách thủ công. Để thực hiện việc này, hãy chạy lệnh ADB sau trên một 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 danh bạ trực tiếp vào thiết bị, nhưng dữ liệu cũng chảy vào Nhà cung cấp danh bạ từ các dịch vụ web thông qua trình chuyển đổi đồng bộ hoá. Trình chuyển đổi này tự động hoá việc chuyển dữ liệu giữa thiết bị và các dịch vụ. Bộ chuyển đổi đồ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à trình chuyển đổi đồng bộ hoá hoạt động được xác định bằng loại tài khoản. Mỗi bộ chuyển đổi đồ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 tài khoản đó. 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ả mối quan hệ giữa loại tài khoản và tên tài khoản với trình chuyển đổi và dịch vụ đồ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. Trong hầu hết trường hợp, người dùng phải xác thực với 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 Danh bạ Google giống với 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 chỉ có một từ hoặc mã nhận dạng dạng số.

Loại tài khoản không cần phải là duy nhất. Người dùng có thể định cấu hình nhiều tài khoản Google Contacts và tải dữ liệu của họ xuống 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 nhau, các thành phần này xác định một luồng dữ liệu cụ thể giữa 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 Nhà cung cấp danh bạ, bạn cần viết trình chuyển đổi đồ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ộ chuyển đổi đồng bộ hoá Nhà cung cấp danh bạ.

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

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

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

Các ứ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 vào một hoặc nhiều bảng
READ_CONTACTS, được chỉ định trong AndroidManifest.xml với phần tử <uses-permission><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 với phần tử <uses-permission><uses-permission android:name="android.permission.WRITE_CONTACTS">.

Các quyền này không mở rộng sang dữ liệu hồ sơ người dùng. Hồ sơ người dùng và các quyền cần thiết của hồ sơ đó sẽ được thảo luận trong phần sau, Hồ sơ người dùng.

Hãy nhớ rằng dữ liệu danh bạ của người dùng mang tính cá nhân 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á thấp ứng dụng của bạn 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 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ị thay vì một trong các người 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 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ó sẵn 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 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 quyền android.Manifest.permission#READ_PROFILE và android.Manifest.permission#WRITE_PROFILE tương ứng để đọc và ghi.

Hãy nhớ rằng bạn nên coi hồ sơ của 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 tiêu chí lựa chọn nào. Bạn cũng có thể sử 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 các hàng đó có phải là hồ sơ người dùng hay không, hãy kiểm thử 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, trong đó có các hàng trong bảng Danh bạ thô, Dữ liệu và Danh bạ, bảng ContactsContract.Settings và bảng ContactsContract.SyncState. Bảng sau đây cho biết tác độ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 nhất. Đánh dấu những người liên hệ thô đã thay đổi trên thiết bị và phải được đồng bộ hoá trở lại vớ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.

Bộ chuyển đổi đồng bộ hoá sửa đổi bảng dữ liệu hoặc thông tin 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 giúp nhà cung cấp không đánh dấu các hàng là không sạch. Nếu không, các nội dung sửa đổi đối với bộ chuyển đổi đồng bộ hoá sẽ có vẻ như là nội dung sửa đổi cục bộ và được gửi đến máy chủ, mặc dù 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 được thay đổi.
ContactsContract.RawContacts SOURCE_ID Một giá trị chuỗi xác định duy nhất thông tin liên hệ thô này với tài khoản mà liên hệ được tạo. Khi một 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 sẽ báo hiệu cho trình chuyển đổi đồng bộ hoá rằng trình chuyển đổi này sẽ tạo một liên hệ thô mới trên máy chủ và nhận giá trị cho SOURCE_ID.

Cụ thể, mã nguồn phải riêng biệt cho mỗi loại tài khoản và phải ổn định trong quá trình đồng bộ hoá:

  • Duy nhất: Mỗi thông tin liên hệ 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 sự cố trong ứng dụng danh bạ. Lưu ý rằng hai địa chỉ liên hệ thô cho cùng một loại tài khoản có thể có cùng một mã nguồn. Ví dụ: thông tin liên hệ 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 thông tin liên hệ thô "Thomas Higginson" cho tài khoản emilyd@gmail.com.
  • Ổn định: Mã nguồn là một phần không thể thiếu trong dữ liệu của dịch vụ trực tuyến đối với thông tin liên hệ 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 địa chỉ liên hệ thô được khôi phục sẽ có cùng mã nguồn như trước đây. Nếu bạn không thực thi điều này, các lối tắt sẽ ngừng hoạt động.
ContactsContract.Groups GROUP_VISIBLE "0" – Không được hiển thị danh bạ trong nhóm này trong giao diện người dùng của ứng dụng Android. Cột này thể hiện khả năng tương thích với các máy chủ cho phép người dùng ẩn người liên hệ trong các nhóm nhất định.
"1" – Cho phép hiển thị danh bạ trong nhóm này 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, các liên hệ không thuộc một nhóm sẽ không hiển thị 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 hiển thị nếu không có người liên hệ thô nào thuộc một nhóm (Thông tin về 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 bảng ContactsContract.Settings cho một loại tài khoản và tài khoản, bạn có thể buộc hiển thị những người liên hệ không có nhóm. Một cách sử dụng cờ này là hiển thị danh bạ từ các máy chủ không sử dụng nhóm.
"1" – Đối với tài khoản và loại tài khoản này, giao diện người dùng của ứng dụng sẽ thấy được những người liên hệ không thuộc một nhóm.
ContactsContract.SyncState (tất cả) Sử dụng bảng này để lưu trữ siêu dữ liệu cho bộ chuyển đổi đồng bộ hoá. Với bảng này, bạn có thể lưu trữ trạng thái đồng bộ hoá và các dữ liệu khác liên quan đến đồng bộ hoá một cách liên tục 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 dữ liệu từ Nhà cung cấp danh bạ, tập trung vào những nội dung sau:

  • Truy vấn 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ừ trình chuyển đổi đồng bộ hoá cũng được đề cập chi tiết hơn trong phần Trình chuyển đổi đồng bộ hoá của Trình cung cấp danh bạ.

Truy vấn thực thể

Vì các bảng của 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 bạn truy xuất một hàng và mọi hàng "con" được liên kết với hàng đó. Ví dụ: để hiển thị tất cả thông tin về một người, bạn có thể 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. Để hỗ trợ việc này, Trình cung cấp danh bạ cung cấp các cấu trúc thực thể, hoạt động như các mối nối cơ sở dữ liệu giữa các bảng.

Thực thể giống như một bảng gồm các cột đã chọn trong bảng mẹ và bảng con. 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 trong 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 một tên mục liên hệ và tất cả hàng ContactsContract.CommonDataKinds.Email của tất cả danh bạ thô của tên đó, bạn sẽ lấy lại Cursor chứa một hàng cho mỗi hàng ContactsContract.CommonDataKinds.Email.

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 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, sau đó 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ạ xử lý một truy vấn dựa trên một thực thể trong một giao dịch duy nhất, đảm bảo rằng dữ liệu được truy xuất nhất quán nội bộ.

Lưu ý: Một thực thể thường 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 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 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 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ó hai hoạt động là "chính" và "chi tiết". Hoạt động chính hiển thị 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 liên hệ thô được liên kết vớ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 tải xong, LoaderManager sẽ gọi một lệnh gọi lại đến onLoadFinished(). Một trong các đối số đến cho phương thức này là Cursor chứa 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 dữ liệu đó.

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 ArrayList của các đối tượng ContentProviderOperation và gọi applyBatch(). Vì Nhà cung cấp danh bạ thực hiện tất cả các thao tác trong một 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ờ khiến kho lưu trữ danh bạ ở trạng thái không nhất quán. Việc sửa đổi hàng loạt cũng tạo điều kiện cho việc chèn một người liên hệ thô và dữ liệu chi tiết của người liên hệ đó cùng một lúc.

Lưu ý: Để sửa đổi một danh bạ thô, hãy cân nhắ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 nhường

Một quá trình sửa đổi hàng loạt chứa một số lượng lớn thao tác có thể chặn các quy trình khác, dẫn đến trải nghiệm tổng thể không tốt cho người dùng. Để sắp xếp tất cả các thao tác 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 những thao tác này chặn hệ thống, bạn nên đặt điểm lợi nhuận cho một hoặc nhiều hoạt động. Điểm lợi nhuận là một đối tượng ContentProviderOperation có giá trị isYieldAllowed() được đặt thành true. Khi gặp điểm nhường, Trình cung cấp danh bạ sẽ tạm dừng công việc để cho phép các quy trình khác chạy và đóng giao dịch hiện tại. Khi khởi động lại, trình cung cấp sẽ tiếp tục thao tác tiếp theo trong ArrayList và bắt đầu một giao dịch mới.

Điểm lợi tức dẫn đến nhiều giao dịch cho mỗi lệnh gọi đến applyBatch(). Do đó, bạn nên đặt điểm trả về cho thao tác cuối cùng cho một tập hợp các hàng có liên quan. Ví dụ: bạn nên đặt điểm trả về cho thao tác cuối cùng trong một tập hợp thêm hàng liên hệ thô và các hàng dữ liệu liên kết với hàng đó, 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 người liên hệ.

Điểm trả về cũng là một đơn vị của thao tác nguyên tử. Tất cả các quyền truy cập giữa hai điểm trả về 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 điểm trả về nào, thì thao tác nguyên tử nhỏ nhất sẽ là toàn bộ lô thao tác. Nếu sử dụng điểm trả về, bạn sẽ ngăn các hoạt động làm giảm hiệu suất hệ thống, đồng thời đảm bảo rằng một tập hợp con các hoạt động là nguyên tử.

Tham chiếu ngược sửa đổi

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 với hàng đó dưới dạng một tập hợp đố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ô dưới dạng giá trị RAW_CONTACT_ID. Tuy nhiên, giá trị này không có sẵn khi bạn tạo ContentProviderOperation cho hàng dữ liệu vì bạn chưa áp dụng ContentProviderOperation cho hàng thông tin 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 phép toán 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 dựa trên 0 của một giá trị trong mảng đố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à lấy lại giá trị _ID của bản ghi đó, sau đó "tham chiếu lại" đến giá trị đó khi bạn thêm 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 phép toán 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 người liên hệ thô và dữ liệu mới theo lô. Các lớp này bao gồm mã thiết lập một điểm lợi nhuận và sử dụng 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 cần thêm thông tin 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ã sẽ tạo các hàng dữ liệu cho các hàng tên hiển thị, điện thoại và email.

Mỗi đối tượng trình tạo toán tử 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. Do đó, mỗi hàng dữ liệu sẽ tự động được liên kết bằng RAW_CONTACT_ID của hàng đó với hàng ContactsContract.RawContacts mới mà hàng dữ liệu đó thuộc về.

Đối tượng ContentProviderOperation.Builder thêm hàng email được gắn cờ bằng withYieldAllowed(), giúp đặt điểm trả về:

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() sẽ chèn hàng dữ liệu và người liên hệ thô 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 hàng loạt cũng cho phép bạn triển khai 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, sau đó kiểm tra những nội dung sửa đổi khác có thể đã được thực hiện cùng lúc. Nếu phát hiện có một nội dung sửa đổi không nhất quán, bạn có thể hoàn tác giao dịch và thử lại.

Phương thức kiểm soát đồng thời lạc quan hữu ích cho thiết bị di động, trong đó 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 tính năng khoá, nên bạn sẽ không mất thời gian thiết lập khoá hoặc chờ các giao dịch khác giải phóng khoá.

Để sử dụng tính năng kiểm soát đồng thời lạc quan trong khi cập nhật một hàng ContactsContract.RawContacts, 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 các 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 quy tắc ràng buộc bằng phương thức newAssertQuery(Uri). Đối với URI nội dung, hãy sử 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 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ỉ một dòng được kiểm thử bằng câu nhận đị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à thời điểm bạn cố gắng sửa đổi hàng đó, thì thao tác "xác nhận" ContentProviderOperation sẽ không thành công và toàn bộ lô thao tác sẽ được huỷ. Sau đó, bạn có thể chọn thử lại lô 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 ContentProviderOperation "xác nhận" sau khi truy vấn một liên hệ thô bằ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 ý đị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 sẽ 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ể làm việ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 dụng của bạn để tiếp tục công việc.
  • Chỉnh sửa dữ liệu của một địa chỉ liên hệ hiện có.
  • Chèn một người liên hệ mới chưa qua sàng lọc 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 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 và gửi dữ liệu đó như một phần của ý định.

Khi sử dụng ý định để truy cập vào 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 phải viết giao diện người dùng hoặc mã của riêng mình để truy cập vào 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 cho bạn quyền đọc đối với một người liên hệ. Vì bạn đang sửa đổi trì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 của việc gửi ý định truy cập vào 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". Thao tác, loại MIME và giá trị dữ liệu mà bạn dùng cho các tác vụ hiện có được tóm tắt trong Bảng 4, trong khi các giá trị bổ sung bạn có thể sử dụng với putExtra() được liệt kê trong tài liệu tham khảo về 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: Không được sử dụng Hiển thị danh sách danh bạ thô hoặc danh sách dữ liệu từ một danh bạ thô, tuỳ thuộc vào loại URI nội dung mà bạn cung cấp.

Gọi startActivityForResult() để trả về URI nội dung của hàng đã chọn. Hình thức của URI là URI nội dung của bảng, trong đó LOOKUP_ID của hàng được thêm vào. Ứng dụng danh bạ của thiết bị uỷ quyền 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ề 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 tập hợp đị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 người liên hệ thô mới thêm sẽ được chuyển lại phương thức gọi lại onActivityResult() của hoạt động trong đối số Intent, trong trường "dữ liệu". Để lấy giá trị, hãy gọi getData().
Chỉnh sửa thông tin liên hệ ACTION_EDIT CONTENT_LOOKUP_URI cho người liên hệ. Hoạt động của người chỉnh sửa sẽ cho phép người dùng chỉnh sửa bất kỳ dữ liệu nào liên kết với địa chỉ liên hệ này. Contacts.CONTENT_ITEM_TYPE, một địa chỉ liên hệ. 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 bộ 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ẽ hiển thị. Nếu ứng dụng của bạn hiển thị dữ liệu liên hệ 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ó. thông tin liên hệ,

Lưu ý: Bạn không cần gửi giá trị tên trong 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 tên mới. Hơn nữa, nếu bạn gửi một 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 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 sử 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 dữ liệu và thông tin liên hệ 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 muốn phải chính xác và mới nhất, nên Nhà cung cấp danh bạ có các quy tắc 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ủ những quy tắc này khi sửa đổi dữ liệu danh bạ. 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 ContactsContract.RawContacts hàng bạn thêm.
Hàng ContactsContract.RawContacts không có hàng ContactsContract.CommonDataKinds.StructuredName trong bảng ContactsContract.Data có thể gây ra sự cố 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 mẹ.
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 sự cố với trình chuyển đổi đồ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. Bạn cần đảm bảo rằng ứng dụng của mình chỉ sửa đổi hoặc xoá dữ liệu cho các hàng thuộc về bạn và chỉ chèn dữ liệu có loại và tên tài khoả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ố đó 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 về cảnh báo của trình biên dịch nếu có bất kỳ hằng số nào không còn đượ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 phép 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 theo 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ẽ hiển thị 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 tệp contacts.xml chứa phần tử <ContactsAccountType> và một hoặc nhiều phần tử con <ContactsDataKind>. Đ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ạ

Trình cung cấp danh bạ được thiết kế riêng để 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. Điều 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. Quá trình đồng bộ hoá cũng đảm bảo rằng người dùng có dữ liệu mới nhất, bất kể nguồn bổ sung và thay đổi. Một ưu điểm khác của tính năng đồng bộ hoá là giúp dữ liệu danh bạ luôn sẵn sàng ngay cả khi thiết bị không kết nối mạng.

Mặc dù bạn có thể triển khai tính năng đồ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ợ tự động hoá các tác vụ sau:

  • Đang kiểm tra tình trạng hoạt động của mạng.
  • Lên lịch và thực thi quá trình đồ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 cần cung cấp trình bổ trợ bộ chuyển đổi đồng bộ hoá. Mỗi bộ chuyển đổi đồng bộ hoá là duy nhất cho 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.

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

Bạn triển khai bộ chuyển đổi đồng bộ hoá dưới dạng một lớp con của AbstractThreadedSyncAdapter và cài đặt bộ chuyển đổi đó trong một ứng dụng Android. Hệ thống sẽ tìm hiểu về bộ điều hợp đồng bộ hoá qua các phần tử trong tệp kê khai ứng dụng của bạn, cũng như 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 cho nhà cung cấp nội dung. Các thông tin này cùng nhau xác định duy nhất trình chuyển đổi. Trình chuyển đổi đồ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 chuyển đổi đồng bộ hoá và bật tính năng đồng bộ hoá cho nhà cung cấp nội dung mà trình chuyển đổi đồng bộ hoá đồng bộ hoá. Tại thời điểm đó, hệ thống sẽ bắt đầu quản lý bộ chuyển đổi, gọi bộ chuyển đổi khi cần để đồng bộ hoá giữa trình cung cấp nội dung và máy chủ.

Lưu ý: Việc sử dụng loại tài khoản trong quá trình xác định trình chuyển đổi đồng bộ hoá cho phép hệ thống phát hiện và nhóm các trình chuyển đổi đồng bộ hoá truy cập vào nhiều dịch vụ của cùng một tổ chức. Ví dụ: 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 một Tài khoản Google vào thiết bị, tất cả trình chuyển đổi đồ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 chuyển đổi đồng bộ hoá được liệt kê sẽ đồng bộ hoá với một nhà cung cấp nội dung khác nhau trên thiết bị.

Do 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ự và thường được dùng cùng với khung bộ điều hợp đồng bộ hoá. Khung xác thực sử dụng trình xác thực trình bổ trợ là lớp con của AbstractAccountAuthenticator. Trình xác thực 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 cá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 phản hồi của dịch vụ.

Nếu dịch vụ chấp nhận thông tin xác thực, trình xác thực có thể lưu trữ thông tin xác thực để sử dụng sau. Do khung trình xác thực trình bổ trợ, AccountManager có thể cung cấp quyền truy cập vào bất kỳ mã xác thực nào 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 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 của Android để xác thực.

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

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

Một thành phần Service phản hồi các yêu cầu của hệ thống để liên kết với bộ điều hợp đồng bộ hoá.
Khi muốn chạy quá trình đồng bộ hoá, hệ thống sẽ gọi phương thức onBind() của dịch vụ để lấy 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 xuyên quá 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(). Bạn phải tạo thực thể cho lớp này dưới dạng một singleton.
Một lớp con của Application.
Lớp này đóng vai trò là nhà máy cho singleton của bộ điều hợp đồng bộ hoá. Sử dụng phương thức onCreate() để tạo bản sao của trình chuyển đổi đồng bộ hoá và cung cấp phương thức "getter" tĩnh để trả về singleton cho phương thức onBind() của dịch vụ trình chuyển đổi đồng bộ hoá.
Không bắt buộc: Thành phần Service phản hồi các yêu cầu của 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ụ tạo bản sao đố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 bộ chuyể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ụ để lấy IBinder cho trình xác thực. Điều này cho phép hệ thống thực hiện lệnh gọi nhiều quá 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 giúp 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 sẽ khác nhau tuỳ theo công nghệ máy chủ đang 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ề việc xác thực.
Các tệp XML xác định bộ điều hợp đồng bộ hoá và trình xác thực với hệ thống.
Các thành phần dịch vụ trình chuyển đổi đồng bộ hoá và trình xác thực đượ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 <meta-data> các phần tử con cung cấp dữ liệu cụ thể cho hệ thống:
  • Phần tử <meta-data> cho dịch vụ trình chuyển đổi đồng bộ hoá trỏ đến tệp XML res/xml/syncadapter.xml. Đổi lại, tệp này chỉ định URI cho dịch vụ web sẽ được đồng bộ hoá với Trình cung cấp danh bạ và 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. Đổi lại, tệp này 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 về luồ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ừ mạng xã hội. Bạn có thể viết một trình chuyển đổi đồ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, bạn có thể tích hợp các ứng dụng và dịch vụ mạng xã hội của mình 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 luồng luôn được liên kết với một 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 thông tin 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 đối với thông tin liên hệ thô được liên kết với mục luồng này. Đừng quên đặt giá trị này khi bạn chèn một mục 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 người liên hệ thô được liên kết với mục trong luồng này. Hãy nhớ đặt giá trị này khi bạn chèn một mục 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 luồng:
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: Giá trị android.provider.BaseColumns#_ID của người liên hệ liên kết với mục lưu lượng này.
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: Giá trị android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY của người liên hệ liên kết với mục trong luồng này.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Giá trị android.provider.BaseColumns#_ID của liên hệ thô mà mục trong luồng này liên kết.
android.provider.ContactsContract.StreamItemsColumn#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.StreamItemsColumn#TEXT
Văn bản của mục trong luồng, nội dung do nguồn của mục đăng, hoặc mô tả một số hành động đã tạo ra mục trong luồng. Cột này có thể chứa bất kỳ định dạng và hình ảnh tài nguyên được nhúng nào mà fromHtml() có thể hiển thị. Nhà cung cấp có thể cắt bớt hoặc thu nhỏ 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 chèn hoặc cập nhật mục luồng, ở 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 mục luồng sẽ chịu trách nhiệm duy trì cột này; cột này không được Nhà cung cấp thông tin liên hệ tự động duy trì.

Để hiển thị thông tin nhận dạng cho các mục trong luồng của bạn, hãy sử dụng android.provider.ContactsContract.StreamItemsItems#RES_ICON, android.provider.ContactsContract.StreamItemsItems#RES_LABEL và

Bảng android.provider.contactsContract.StreamItems cũng chứa các cột android.provider.ContactsContract.StreamItemsItems#SYNC1 đến android.provider.ContactsContract.StreamItemsColumn#SYNC4 để dùng được các bộ chuyển đổi đồng bộ hoá riêng biệt.

Ảnh trong luồng nội dung trên mạng xã hội

Bảng android.provider.ContactsContract.StreamItemPhotos lưu trữ các ảnh liên kết với một mục trong luồng. 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 ảnh được lưu trữ trong bảng này ở các cột sau:

Cột android.provider.ContactsContract.StreamItemPhotos#PHOTO (một BLOB).
Một bản trình bày nhị phân của ảnh, do nhà cung cấp đổi kích thước để lưu trữ và hiển thị. Cột này có sẵn để có khả năng 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 sử dụng cột này để lưu trữ ảnh. Thay vào đó, hãy sử 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 ảnh và bạn có thể đọc hình thu nhỏ đó.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Giá trị nhận dạng dạng số của một ả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 URI nội dung trỏ đến một tệp ảnh, sau đó gọi openAssetFileDescriptor() để lấy tên xử lý 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 của ảnh được biểu thị bằng hàng này. Gọi openAssetFileDescriptor() bằng URI này để lấy handle cho tệp ảnh.

Sử dụng bảng luồ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ừ:

  • Những bảng này yêu cầu thêm quyền truy cập. Để đọc được các lệnh 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 chế độ cài đặt đó, ứng dụng của bạn phải có quyền android.Manifest.permission#permission_SOCIAL_STREAM.
  • Đối với bảng android.provider.ContactsContract.StreamItems, số lượng hàng được lưu trữ cho mỗi liên hệ thô bị giới hạn. Khi đạt đến giới hạn này, Nhà cung cấp danh bạ sẽ tạo không gian cho các hàng mục phát trực tuyến mới bằng cách tự động xoá các hàng có android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP cũ nhất. Để biết giới hạn, hãy đưa ra 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 sẽ trả về một Con trỏ chứa một hàng duy nhất, với một cột duy nhất là android.provider.ContactsContract.StreamItems#MAX_ITEMS.

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

Lượt tương tác trên luồng xã hội

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

  • Bằng cách đồng bộ hoá dịch vụ mạng xã hội với Nhà cung cấp danh bạ bằng bộ chuyển đổi đồ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 hoạt động đó vào 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 thường, bạn có thể kích hoạt trình chuyển đổi đồng bộ hoá để truy xuất thêm dữ liệu khi người dùng chọn một người liên hệ để xem. Điều này cho phép bộ chuyển đổi đồng bộ hoá truy xuất ảnh có độ phân giải cao và các mục phát trực tuyến gần đây nhất cho người liên hệ.
  • Bằng cách đăng ký thông báo với ứng dụng danh bạ của thiết bị và Trình cung cấp danh bạ, bạn có thể nhận được một ý định khi một mục liên hệ được xem và cập nhật trạng thái của mục 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 bật tính năng này bằng tính năng "mời liên hệ". Bạn bật tính năng này bằng cách kết hợp một hoạt động thêm một 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à Nhà cung cấp danh bạ thông tin chi tiết về ứng dụng của bạn.

Quá trình đồng bộ hoá định kỳ các mục phát trực tuyến với Nhà cung cấp danh bạ cũng giống như các quá trình đồng bộ hoá khác. Để tìm hiểu thêm về tính năng đồng bộ hoá, hãy xem phần Trình chuyển đổi đồng bộ hoá của 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 lượt xem trên mạng xã hội

Cách đăng ký trình chuyển đổi đồng bộ hoá để nhận thông báo khi người dùng xem một người liên hệ do trình chuyển đổi đồng bộ hoá 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 được ý định từ ứng dụng danh bạ của thiết bị. Đối với dịch vụ thông báo, hãy sử dụng một lớp mở rộng IntentService để cho phép dịch vụ nhận ý định. Dữ liệu trong ý định đến chứa URI nội dung của địa chỉ 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 chuyển đổi đồng bộ hoá để cập nhật dữ liệu cho danh bạ thô.

Cách đăng ký một hoạt động để được gọi khi người dùng nhấp vào một mục trong luồng hoặc ả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ạ trên thiết bị.
  4. Để đă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 ả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 được ý đị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à cho ả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 của bạn

Người dùng không cần phải rời khỏi ứng dụng danh bạ của thiết bị để mời một người liên hệ đến 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 được ý định. Giá trị invite_action_label là một chuỗi văn bản hiển thị trong trình đơn Add Connection (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 được 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à 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ạ. Phương thức này có cú pháp 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 các 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.

Lưu ý rằng tiền tố thuộc tính android: không bắt buộc đối với 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 từ ứng dụng danh bạ của thiết bị.
inviteContactActionLabel
Một chuỗi văn bản hiển thị cho hoạt động được chỉ định trong inviteContactActivity, trong trình đơn Thêm 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. Dịch vụ này sẽ nhận được thông báo khi người dùng xem một 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 tốn 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 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 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ẽ hiển thị.
viewGroupActionLabel
Nhãn mà ứng dụng danh bạ hiển thị cho một thành phần điều khiển 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.

Bạn có thể sử dụng giá trị nhận dạng tài nguyên chuỗi 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ị chạy khi người dùng nhấp vào một mục trong luồng cho một 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ị chạy khi người dùng nhấp vào một bức ảnh trong mục luồng cho một người liên hệ thô.

Phần tử <ContactsDataKind>

Phần tử <ContactsDataKind> kiểm soát chế độ 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ạ. Phương thức này có cú pháp 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 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à bộ chuyển đổi đồng bộ hoá sẽ 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ó 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
Tài nguyên có thể vẽ của Android mà ứng dụng danh bạ hiển thị bên cạnh dữ liệu của bạn. Hãy dùng thông tin 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 của giá trị đầu tiên trong số hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này sẽ hiển thị 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 không bắt buộc. Hãy xem thêm android:detailColumn.
android:detailColumn
Tên cột cho giá trị thứ hai trong số hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này hiển thị là 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ạ

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

  • Nhóm địa chỉ liên hệ
  • Tính năng cho ảnh

Nhóm địa chỉ liên hệ

Trình cung cấp danh bạ có thể tuỳ ý gắn nhãn cho các tập hợp danh bạ có liên quan bằng dữ liệu nhóm. Nếu máy chủ liên kết với tài khoản người dùng muốn duy trì các nhóm, thì trình chuyển đổi đồ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 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 đặt người liên hệ này vào một nhóm mới, trình chuyển đổi đồng bộ hoá phải thêm nhóm mới vào bảng ContactsContract.Groups. Nhóm hoặc 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, sử dụng loại MIME ContactsContract.CommonDataKinds.GroupMembership.

Nếu bạn đang thiết kế bộ điều hợp đồ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 hiện không sử dụng nhóm, thì bạn cần yêu cầu 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 một tài khoản vào thiết bị, hãy cập nhật hàng ContactsContract.Settings mà Nhà cung cấp danh bạ thêm vào 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ạ, 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 là Photo.CONTENT_ITEM_TYPE. Cột CONTACT_ID của hàng được liên kết với cột _ID của thông tin liên hệ thô mà hàng đó thuộc về. Lớp ContactsContract.Contacts.Photo xác định một bảng con của ContactsContract.Contacts chứa thông tin ả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ệ thô chính. Tương tự, lớp ContactsContract.RawContacts.DisplayPhoto xác định một bảng con của ContactsContract.RawContacts chứa thông tin ảnh cho ảnh chính của một người 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 hình ảnh. Không có lớp tiện lợi nào để truy xuất hình thu nhỏ chính cho một người liên hệ thô, nhưng bạn có thể gửi truy vấn đến bảng ContactsContract.Data, chọn trên _ID, Photo.CONTENT_ITEM_TYPE và cột IS_PRIMARY của người liên hệ thô để tìm hàng ảnh chính của người liên hệ thô.

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