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 người dùng. Trì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 trình cung cấp danh bạ 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 dữ liệu trở nên phức tạp. Do đó, API của trình cung cấp bao gồm một tập hợp phong phú các lớp và giao diện hợp đồng, giúp 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 cơ bản của trình cung cấp.
  • 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 viết bộ điều hợp đồng bộ hoá để đồng bộ hoá dữ liệu từ máy chủ của bạn với Trình cung cấp danh bạ.

Hướng dẫn này giả định rằng bạn đã biết các kiến thức cơ bản về nhà cung cấp nội dung Android. Để tìm hiểu thêm về trình cung cấp nội dung Android, hãy đọc hướng dẫn 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. Ứng dụng 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 của Trình cung cấp danh bạ.

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

Bảng ContactsContract.Contacts
Các hàng đại diện cho những người khác nhau, dựa trên dữ liệu tổng hợp của các hàng liên hệ thô.
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ề 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 được biểu thị bằng các lớp contract trong ContactsContract là các bảng phụ trợ mà Trì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 danh bạ hoặc ứng dụng điện thoại của thiết bị.

Người liên hệ thô

Một liên hệ thô đại diện cho dữ liệu của một người đến từ một loại tài khoản và tên tài khoản duy nhất. Vì Trình cung cấp danh bạ cho phép nhiều dịch vụ trực tuyến làm nguồn dữ liệu cho một người, nên Trình cung cấp danh bạ cho phép nhiều liên hệ thô cho cùng một người. Nhiều 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 cho 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 ContactsContract.RawContacts mẹ.

Các cột quan trọng của liên hệ 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 lưu ý sau bảng:

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

Tên cột Sử dụng Ghi chú
ACCOUNT_NAME Tên tài khoản cho loại tài khoản là nguồn của liên hệ thô này. Ví dụ: tên tài khoản của Tài khoản Google là một trong những địa chỉ Gmail của chủ sở hữu thiết bị. Hãy xem mục tiếp theo về ACCOUNT_TYPE để biết thêm thông tin. Định dạng của tên này tuỳ thuộc vào loại tài khoản. Đây không nhất thiết phải là địa chỉ email.
ACCOUNT_TYPE Loại tài khoản là nguồn của 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 của bạn bằng giá trị nhận dạng miền cho miền mà bạn sở hữu hoặc kiểm soát. Điều này sẽ đảm bảo rằng loại tài khoản của bạn là duy nhất. Loại tài khoản cung cấp dữ liệu danh bạ thường có một bộ điều hợp đồng bộ hoá liên kết để đồ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 trong nội bộ cho đến khi bộ điều hợp đồng bộ hoá có thể xoá hàng khỏi máy chủ của họ, sau đó xoá hàng khỏi kho lưu trữ.

Ghi chú

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

  • Tên của một người liên hệ thô không được lưu trữ trong hàng của người 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 liên hệ thô chỉ có một hàng thuộc loại này trong bảng ContactsContract.Data.
  • Thận trọng: Để sử dụng dữ liệu tài khoản của riêng bạn trong một hàng liên hệ thô, trước tiên, bạn phải đăng ký dữ liệu đó với AccountManager. Để làm 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 việc này, Trình cung cấp danh bạ sẽ tự động xoá hàng liên hệ thô của bạn.

    Ví dụ: nếu bạn muốn ứng dụng của mình duy trì dữ liệu danh bạ cho dịch vụ chạy trên nền tảng web có miền com.example.dataservice và tài khoản của người dùng cho dịch vụ của bạn là becky.sharp@dataservice.example.com, thì trước tiên, người dùng phải thêm "loại" tài khoản (com.example.dataservice) và "tên" tài khoản (becky.smart@dataservice.example.com) 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. 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 liên hệ 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ô ấy:

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

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

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

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

Hãy lưu ý rằng các loại dữ liệu khác nhau được lưu trữ trong một bảng duy nhất này. Các hàng thông tin 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 có tên chung. Nội dung của cột tên mô tả có cùng ý nghĩa bất kể loại dữ liệu trong hàng, trong khi nội dung của cột tên chung có ý nghĩa khác nhau tuỳ thuộc vào loại dữ liệu.

Tên cột mô tả

Sau đây là một số ví dụ về tên cột 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à có thể được sử dụng bởi bất kỳ ứng dụng hoặc bộ điều hợp đồng bộ hoá nào hoạt động với Trình cung cấp danh bạ.
IS_PRIMARY
Nếu loại hàng dữ liệu này có thể xuất hiện nhiều lần cho một người liên hệ thô, thì cột IS_PRIMARY sẽ gắn cờ hàng dữ liệu chứa dữ liệu chính cho loại đó. Ví dụ: nếu người dùng nhấn và giữ một số điện thoại của một người liên hệ rồi chọn Đặt làm mặc định, thì hàng ContactsContract.Data chứa số đó sẽ có cột IS_PRIMARY được đặt thành một giá trị khác 0.

Tên cột chung chung

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

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

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

Tên cột theo loại

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

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

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

Hình 2 cho thấy cách các cột mô tả và cột dữ liệu xuất hiện trong một hàng ContactsContract.Data, cũng như cách tên cột theo loại cụ thể "phủ lên" 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 cụ thể và tên cột chung.

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

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

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

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

Danh bạ

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

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

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

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

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

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

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

Hình 3. Mối quan hệ giữa các bảng Danh bạ, liên hệ 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 đang chạy trên thiết bị Android 10 (cấp độ API 29) trở lên, hãy lưu ý rằng một số trường dữ liệu và phương thức liên hệ đã lỗi thời.

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

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

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

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

Để xác minh rằng chức năng của ứng dụng không bị ảnh hưởng bởi thay đổi này, bạn có thể xoá các trường dữ liệu này theo cách thủ công. Để làm việc này, hãy chạy lệnh adb sau trên thiết bị chạy Android 4.1 (cấp độ API 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 được chuyển vào Trình cung cấp danh bạ từ các dịch vụ web thông qua bộ điều hợp đồng bộ hoá. Bộ điều hợp này tự động hoá việc chuyển dữ liệu giữa thiết bị và dịch vụ. Bộ điều hợp đồng bộ hoá chạy ở chế độ nền dưới sự kiểm soát của hệ thống và gọi các phương thức ContentResolver để quản lý dữ liệu.

Trong Android, dịch vụ web mà bộ điều hợp đồng bộ hoá hoạt động cùng được xác định theo loại tài khoản. Mỗi bộ điều hợp đồng bộ hoá hoạt động với một loại tài khoản, nhưng có thể hỗ trợ nhiều tên tài khoản cho loại đó. 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 liên hệ thô. Các định nghĩa sau đây cung cấp thêm thông tin chi tiết và mô tả mối liên hệ giữa loại tài khoản và tên tài khoản với bộ điều hợp đồng bộ hoá và dịch vụ.

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

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

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

Hình 4 cho thấy cách 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 dấu là "bộ điều hợp đồng bộ hoá", mỗi bộ điều hợp được gắn nhãn theo loại tài khoản.

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

Các quyền này không áp dụng cho dữ liệu hồ sơ người dùng. Hồ sơ người dùng và các quyền cần thiết đượ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 là dữ liệu cá nhân và nhạy cảm. Người dùng quan tâm đến 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 hiểu rõ lý do bạn cần quyền truy cập vào dữ liệu danh bạ của họ, thì họ có thể đánh giá ứng dụng của bạn ở mức thấp hoặc đơn giản là từ chối cài đặt ứng dụng.

Hồ sơ người dùng

Bảng ContactsContract.Contacts có một hàng 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 những 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 một hàng người liên hệ thô cho mỗi hệ thống sử dụng hồ sơ. Mỗi hàng 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 các quyền READ_CONTACTS và WRITE_CONTACTS cần thiết để đọc và ghi, quyền truy cập vào hồ sơ người dùng lần lượt cần có quyền android.Manifest.permission#READ_PROFILE và android.Manifest.permission#WRITE_PROFILE để đọ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 bất kỳ tiêu chí lựa chọn nào. Bạn cũng có thể dùng URI nội dung này làm URI cơ sở để truy xuất danh bạ thô hoặc dữ liệu cho hồ sơ. Ví dụ: đoạn mã này truy xuất dữ liệu cho hồ sơ:

Kotlin

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

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

Java

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

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

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

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

Trình cung cấp danh bạ quản lý dữ liệu theo dõi trạng thái của dữ liệu danh bạ trong kho lưu trữ. Siêu dữ liệu này về kho lưu trữ được lưu trữ ở nhiều nơi, bao gồm cả các hàng trong bảng Liên hệ thô, Dữ liệu và Danh bạ, bảng ContactsContract.Settings và bảng ContactsContract.SyncState. Bảng sau đây cho thấy hiệu quả 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àn Cột Giá trị Ý nghĩa
ContactsContract.RawContacts DIRTY "0" – không thay đổi kể từ lần đồng bộ hoá gần đây nhất. Đánh dấu những người liên hệ thô đã thay đổi trên thiết bị và phải được đồng bộ hoá trở lại máy chủ. Giá trị này được Trình cung cấp danh bạ tự động đặt khi các ứng dụng Android cập nhật một hàng.

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

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

  • Duy nhất: Mỗi người 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 điều này, bạn sẽ gây ra sự cố trong ứng dụng danh bạ. Lưu ý rằng hai người liên hệ thô cho cùng một loại tài khoản có thể có cùng mã nguồn. Ví dụ: liên hệ thô "Thomas Higginson" cho tài khoản emily.dickinson@gmail.com được phép có cùng mã nguồn với liên hệ thô "Thomas Higginson" cho tài khoản emilyd@gmail.com.
  • Ổn định: Mã nguồn là một phần vĩnh viễn trong dữ liệu của dịch vụ trực tuyến cho 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 liên hệ thô được khôi phục phải có cùng mã nguồn như trước. 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" – Người liên hệ trong nhóm này không được xuất hiện trong giao diện người dùng của ứng dụng Android. Cột này dùng để tương thích với các máy chủ cho phép người dùng ẩn người liên hệ trong một số nhóm nhất định.
"1" – Người liên hệ trong nhóm này được phép xuất hiện trong giao diện người dùng của ứng dụng.
ContactsContract.Settings UNGROUPED_VISIBLE "0" – Đối với tài khoản và loại tài khoản này, những người liên hệ không thuộc nhóm sẽ không hiển thị trong 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 (Tư cách thành viên nhóm của một người liên hệ thô được biểu thị bằng một hoặc nhiều hàng ContactsContract.CommonDataKinds.GroupMembership trong bảng ContactsContract.Data). Bằng cách đặt cờ này trong hàng của bảng ContactsContract.Settings cho một loại tài khoản và tài khoản, bạn có thể buộc các liên hệ không có nhóm phải hiển thị. 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, các danh bạ không thuộc nhóm nào sẽ hiển thị trên giao diện người dùng của ứng dụng.
ContactsContract.SyncState (tất cả) Sử dụng bảng này để lưu trữ siêu dữ liệu cho bộ điều hợp đồng bộ hoá. Với bảng này, bạn có thể lưu trữ trạng thái đồng bộ hoá và dữ liệu khác liên quan đến đồng bộ hoá một cách ổn định 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ừ Trình cung cấp danh bạ, tập trung vào những nội dung sau:

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

Việc sửa đổi từ bộ điều hợp đồng bộ hoá cũng được đề cập chi tiết hơn trong phần Bộ điều hợp đồng bộ hoá 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 việc truy xuất một hàng và tất cả các hàng "con" được liên kết với hàng đó thường rất hữu ích. Ví dụ: để hiển thị tất cả thông tin về một người, bạn có thể muốn truy xuất tất cả các hàng ContactsContract.RawContacts cho một hàng ContactsContract.Contacts hoặc tất cả các hàng ContactsContract.CommonDataKinds.Email cho một hàng ContactsContract.RawContacts. Để 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 giống như các phép nối cơ sở dữ liệu giữa các bảng.

Thực thể giống như một bảng bao gồm các cột được chọn từ bảng mẹ và bảng con. Khi truy vấn một thực thể, bạn 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 từ 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 để tìm tên người liên hệ và tất cả các hàng ContactsContract.CommonDataKinds.Email cho tất cả người liên hệ thô có tên đó, thì bạn sẽ nhận lại một 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 danh bạ cho một người liên hệ hoặc người liên hệ thô cùng một lúc, thay vì phải truy vấn bảng mẹ trước để lấy mã nhận dạng, 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ạ còn xử lý một truy vấn đối với một thực thể trong một giao dịch, đảm bảo rằng dữ liệu được truy xuất nhất quán trong 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 làm việc với 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ả hàng liên hệ thô cho một người liên hệ. Đoạn mã này là một phần của ứng dụng lớn hơn có hai hoạt động, "chính" và "chi tiết". Hoạt động chính cho thấy danh sách các hàng liên hệ; khi người dùng chọn một hàng, hoạt động sẽ gửi mã nhận dạng của hàng đó đến hoạt động chi tiết. Hoạt động chi tiết sử dụng ContactsContract.Contacts.Entity để hiển thị tất cả các hàng dữ liệu từ tất cả các liên hệ thô 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 quá trình tải hoàn tất, LoaderManager sẽ gọi lệnh gọi lại đến onLoadFinished(). Một trong các đối số truyền vào phương thức này là một Cursor có kết quả của truy vấn. Trong ứng dụng của riêng mình, bạn có thể lấy dữ liệu từ Cursor này để hiển thị hoặc xử lý thêm.

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

Bất cứ khi nào có thể, bạn nên chèn, cập nhật và xoá dữ liệu trong Trình cung cấp danh bạ ở "chế độ hàng loạt" bằng cách tạo một ArrayList của đối tượng ContentProviderOperation và gọi applyBatch(). Vì Trình cung cấp danh bạ thực hiện tất cả các thao tác trong một applyBatch() trong một giao dịch, 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 hỗ trợ việc chèn một liên hệ thô và dữ liệu chi tiết của liên hệ đó cùng một lúc.

Lưu ý: Để sửa đổi một người liên hệ thô, hãy cân nhắc gửi ý đị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. Cách làm này được mô tả chi tiết hơn trong phần Truy xuất và sửa đổi bằng ý định.

Điểm dừng

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

Điểm dừng 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 dừng cho thao tác cuối cùng đối với một tập hợp các hàng có liên quan. Ví dụ: bạn nên đặt điểm dừng 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 quan, hoặc thao tác cuối cùng cho một tập hợp hàng liên quan đến một người liên hệ.

Điểm dừng 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 dừng sẽ thành công hoặc không thành công dưới dạng một đơn vị duy nhất. Nếu bạn không đặt bất kỳ điểm dừng nào, thì thao tác nguyên tử nhỏ nhất sẽ là toàn bộ lô thao tác. Nếu sử dụng điểm dừng, bạn sẽ ngăn các thao tác làm giảm hiệu suất hệ thống, đồng thời đảm bảo rằng một nhóm nhỏ các thao tác là nguyên tử.

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

Phương thức withValueBackReference() có hai đố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 theo lô đượ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 các 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 đó tạo một "tham chiếu ngược" đến giá trị đó khi bạn thêm một hàng ContactsContract.Data.

Toàn bộ mảng kết quả được tạo khi bạn gọi applyBatch() lần đầu tiên, với kích thước bằng kích thước của ArrayList đối tượng ContentProviderOperation mà bạn cung cấp. Tuy nhiên, tất 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 kết quả cho một thao tác chưa được áp dụng, withValueBackReference() sẽ gửi một Exception.

Các đoạn mã sau đây cho biết cách chèn một liên hệ thô và dữ liệu mới theo lô. Các ví dụ này bao gồm mã thiết lập điểm dừng 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 mà bạn nên thêm người liên hệ thô mới.

Kotlin

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

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

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

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

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

Đoạn mã tiếp theo 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 tên hiển thị, số điện thoại và email.

Mỗi đối tượng trình tạo thao tác đều sử dụng withValueBackReference() để lấy RAW_CONTACT_ID. Tham chiếu trỏ lại đối tượng ContentProviderResult từ thao tác đầu tiên, thao tác này 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 đó 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 dừng:

Kotlin

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

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

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

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

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

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

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

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

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

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

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

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

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

Java

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

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

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

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

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

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

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

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

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

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

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

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

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

Đoạn mã cuối cùng cho thấy lệnh gọi đến applyBatch() để chèn các hàng dữ liệu và 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);
    }
}

Thao tác theo lô cũng cho phép bạn triển khai kiểm soát đồng thời lạc quan, một phương thức áp dụng các giao dịch sửa đổi mà không cần khoá kho lưu trữ cơ bản. Để sử dụng phương thức này, bạn áp dụng giao dịch rồi kiểm tra các 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 thấy có sự sửa đổi không nhất quán, bạn sẽ khôi phục giao dịch và thử lại.

Kiểm soát đồng thời lạc quan rất hữu ích cho thiết bị di động, nơi chỉ có một người dùng tại một thời điểm và hiếm khi có nhiều người dùng 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 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 người liên hệ thô cùng với dữ liệu khác mà bạn truy xuất.
  2. Tạo một đối tượng ContentProviderOperation.Builder phù hợp để thực thi một quy tắc ràng buộc, sử dụng phương thức newAssertQuery(Uri). Đối với URI nội dung, hãy sử dụng RawContacts.CONTENT_URI có thêm _ID của người liên hệ thô.
  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 câu nhận định này chỉ kiểm thử một hàng.
  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 theo lô.

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 gian bạn cố gắng sửa đổi hàng, thì ContentProviderOperation "xác nhận" sẽ không thành công và toàn bộ lô thao tác sẽ bị huỷ. Sau đó, bạn có thể chọn thử lại gói hoặc thực hiện một số hành động 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ể thực hiện các công 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 để xử lý thêm.
  • Chỉnh sửa dữ liệu của một người liên hệ hiện có.
  • Chèn một người liên hệ thô mới cho bất kỳ tài khoản nào của họ.
  • Xoá dữ liệu của một hoặc nhiều người liên hệ.

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

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

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

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

Nhiệm vụ Hành động 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 người liên hệ thô hoặc danh sách dữ liệu từ một người liên hệ 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. Dạng của URI là URI nội dung của bảng có thêm LOOKUP_ID của hàng. Ứng dụng Danh bạ của thiết bị sẽ uỷ quyền quyền đọc và quyền 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 về Kiến thức cơ bản về Trình cung cấp nội dung để biết thêm thông tin chi tiết.

Chèn một người liên hệ thô mới Insert.ACTION Không áp dụng RawContacts.CONTENT_TYPE, loại MIME cho một nhóm người 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 liên hệ thô mới thêm sẽ được truyền trở lại phương thức gọi lại onActivityResult() của hoạt động trong đối số Intent, trong trường "dữ liệu". Để lấy giá trị, hãy gọi getData().
Chỉnh sửa thông tin về một người liên hệ ACTION_EDIT CONTENT_LOOKUP_URI cho người liên hệ. Hoạt động của trình 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 quan đến người liên hệ này. Contacts.CONTENT_ITEM_TYPE, một người 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 Done (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 người liên hệ mới. Màn hình chỉnh sửa hoặc 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ẽ được 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ó. 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 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 liên hệ thô và dữ liệu 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 là chính xác và cập nhật, nên Trình cung cấp danh bạ có các quy tắc được xác định rõ ràng về tính toàn vẹn của dữ liệu. Bạn có trách nhiệm tuân thủ các quy tắc này khi sửa đổi dữ liệu 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 hàng ContactsContract.RawContacts mà bạn thêm.
Một hàng ContactsContract.RawContacts không có hàng ContactsContract.CommonDataKinds.StructuredName trong bảng ContactsContract.Data có thể gây ra vấn đề trong quá trình tổng hợp.
Luôn liên kết các hàng ContactsContract.Data mới với hàng ContactsContract.RawContacts 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 bộ điều hợp đồng bộ hoá.
Chỉ thay đổi dữ liệu cho những người liên hệ thô mà bạn sở hữu.
Hãy nhớ rằng Trình cung cấp danh bạ thường quản lý dữ liệu từ một số loại tài khoản/dịch vụ trực tuyến khác nhau. Bạn cần đảm bảo rằng ứng dụng của mình chỉ sửa đổi hoặc xoá dữ liệu cho các hàng thuộc về bạn, đồng thời chỉ chèn dữ liệu có loại tài khoản và tên mà bạn kiểm soát.
Luôn sử dụng các hằng số được xác định trong ContactsContract và các lớp con của hằng số đó cho các giá trị của cơ quan quản lý, URI nội dung, đường dẫn URI, tên cột, loại MIME và TYPE.
Việc sử dụng các hằng số này giúp bạn tránh được lỗi. Bạn cũng sẽ nhận được thông báo kèm theo cảnh báo của trình biên dịch nếu có hằng số nào không được dùng nữa.

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

Bằng cách tạo và sử dụng các loại MIME tuỳ chỉnh của riêng mình, bạn có thể chèn, chỉnh sửa, xoá và truy xuất các hàng dữ liệu của riêng mình trong bảng ContactsContract.Data. Các hàng của bạn chỉ được sử dụng cột được xác định trong ContactsContract.DataColumns, mặc dù bạn có thể liên kết tên cột 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 của 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á của 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 thiết bị và 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 thiết bị mới và tải dữ liệu hiện có lên 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 lợi ích khác của việc đồng bộ hoá là giúp dữ liệu danh bạ luôn có sẵn ngay cả khi thiết bị không kết nối với mạng.

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

  • Đang kiểm tra xem có mạng hay không.
  • Lên lịch và thực hiện đồng bộ hoá, dựa trên lựa chọn ưu tiên của người dùng.
  • Đang 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 một trình bổ trợ bộ điều hợp đồng bộ hoá. Mỗi bộ điều hợp đồng bộ hoá là duy nhất cho một dịch vụ và nhà cung cấp nội dung, nhưng có thể xử lý nhiều tên tài khoản cho cùng một dịch vụ. Khung này cũng cho phép nhiều bộ điều hợp đồng bộ hoá cho cùng một dịch vụ và nhà cung cấp.

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

Bạn triển khai bộ điều hợp đồng bộ hoá dưới dạng một lớp con của AbstractThreadedSyncAdapter và cài đặt bộ điều hợp đó dưới dạng một phần của ứng dụng Android. Hệ thống tìm hiểu về bộ điều hợp đồng bộ hoá từ các phần tử trong tệp kê khai ứng dụng và từ một tệp XML đặc biệt mà tệp kê khai trỏ đến. Tệp XML xác định loại tài khoản cho dịch vụ trực tuyến và đơn vị quản lý cho trình cung cấp nội dung, cả hai đều xác định duy nhất bộ chuyển đổi. Bộ điều hợp đồng bộ hoá sẽ không hoạt động cho đến khi người dùng thêm một tài khoản cho loại tài khoản của bộ điều hợp đồng bộ hoá và bật tính năng đồng bộ hoá cho trình cung cấp nội dung mà bộ điều hợp đồng bộ hoá đồng bộ hoá. Khi đó, hệ thống sẽ bắt đầu quản lý bộ chuyển đổi, gọi bộ chuyển đổi khi cần để đồ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 làm một phần của thông tin nhận dạng bộ điều hợp đồng bộ hoá cho phép hệ thống phát hiện và nhóm các bộ điều hợp đồng bộ hoá truy cập vào các dịch vụ khác nhau từ cùng một tổ chức. Ví dụ: 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 loại tài khoản com.google. Khi người dùng thêm Tài khoản Google vào thiết bị, tất cả bộ điều hợp đồng bộ hoá đã cài đặt cho các dịch vụ của Google sẽ được liệt kê cùng nhau; mỗi bộ điều hợp đồng bộ hoá được liệt kê sẽ đồng bộ hoá với một nhà cung cấp nội dung khác trên thiết bị.

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

  1. Thu thập tên, mật khẩu hoặc thông tin tương tự của người dùng (thông tin đăng nhập của người dùng).
  2. Gửi thông tin đăng nhập đế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, thì trình xác thực có thể lưu trữ thông tin xác thực để sử dụng sau này. Do khung trình xác thực trình bổ trợ, AccountManager có thể cấp quyền truy cập vào bất kỳ mã thông báo 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ã thông báo xác thực OAuth2.

Mặc dù không bắt buộc phải xác thực, nhưng hầu hết các dịch vụ danh bạ đều sử dụng tính năng này. Tuy nhiên, bạn không bắt buộc phải sử dụng khung xác thực Android để thực hiện việc 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 Trình cung cấp danh bạ, bạn bắt đầu bằng cách tạo một ứng dụng Android chứa những nội dung sau:

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

Dữ liệu 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 bộ điều hợp đồng bộ hoá để thêm dữ liệu luồng từ mạng của riêng bạn vào các bảng này, hoặc bạn có thể đọc dữ liệu luồng từ các bảng này và hiển thị dữ liệu đó trong ứng dụng của riêng bạn, hoặc cả hai. Với các tính năng này, bạn có thể tích hợp các dịch vụ và ứng dụng mạng xã hội vào trải nghiệm mạng xã hội của Android.

Văn bản luồ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 với giá trị _ID cho người liên hệ thô. Loại tài khoản và tên tài khoản của người liên hệ thô cũng được lưu trữ trong hàng mục luồng.

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

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

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

Bảng android.provider.ContactsContract.StreamItems cũng chứa các cột android.provider.ContactsContract.StreamItemsColumns#SYNC1 đến android.provider.ContactsContract.StreamItemsColumns#SYNC4 để sử dụng riêng cho bộ điều hợp đồng bộ hoá.

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

Bảng android.provider.ContactsContract.StreamItemPhotos lưu trữ ả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. Tham chiếu ảnh được lưu trữ trong bảng ở các cột sau:

Cột android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
Biểu diễn nhị phân của ảnh, được nhà cung cấp đổi kích thước để lưu trữ và hiển thị. Cột này có sẵn để 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ạ đã sử 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, có thể đọc được.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Giá trị nhận dạng dạng số của ảnh cho một người liên hệ thô. Thêm 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 người dùng cho tệp ảnh.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
URI nội dung trỏ trực tiếp đến tệp ảnh cho ảnh được hàng này đại diện. Gọi openAssetFileDescriptor() bằng URI này để lấy tên người dùng cho tệp ảnh.

Sử dụng bảng luồng mạng xã hội

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

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

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

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

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

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

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

Đăng ký xử lý các chế độ xem mạng xã hội

Cách đăng ký bộ điều hợp đồng bộ hoá để nhận thông báo khi người dùng xem một người liên hệ do bộ điều hợp đồ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 thông tin chi tiết của một người liên hệ trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewContactNotifyService="serviceclass" vào phần tử, trong đó serviceclass là tên lớp đủ điều kiện của dịch vụ sẽ nhận ý định từ ứng dụng danh bạ của thiết bị. Đối với dịch vụ thông báo, hãy 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 người 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 bộ điều hợp đồng bộ hoá để cập nhật dữ liệu cho người liên hệ 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ạ của 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 luồng trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewStreamItemPhotoActivity="activityclass" vào phần tử, trong đó activityclass là tên lớp đủ điều kiện của hoạt động sẽ nhận ý định từ ứng dụng danh bạ của thiết bị.

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

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

Tương tác với dịch vụ mạng xã hội 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ệ tham gia trang mạng xã hội của bạn. Thay vào đó, bạn có thể yêu cầu ứng dụng danh bạ của thiết bị gửi một ý định mời người liên hệ tham gia một trong các hoạt động của bạn. Cách thiết lập chế độ này:

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

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

Tài liệu tham khảo về 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 của bạn với ứng dụng danh bạ và Trình cung cấp danh bạ. Các phần tử này được mô tả trong những 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ạ. Tham số này có cú pháp như sau:

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

có trong:

res/xml/contacts.xml

có thể chứa:

<ContactsDataKind>

Nội dung mô tả:

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 những người liên hệ của họ tham gia mạng xã hội, thông báo cho người dùng khi một trong các luồng mạng xã hội của họ được cập nhật, v.v.

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

Thuộc tính:

inviteContactActivity
Tên lớp đủ điều kiện của hoạt động trong ứng dụng mà bạn muốn kích hoạt khi người dùng chọn Thêm kết nối trong ứng dụng danh bạ của thiết bị.
inviteContactActionLabel
Một chuỗi văn bản 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 sẽ nhận được thông báo khi người dùng xem một người 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 cần nhiều dữ liệu cho đến khi cần thiết. 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ệ và các mục gần đây nhất trong luồng 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 với luồng mạng xã hội.
viewGroupActivity
Tên lớp đủ điều kiện của một hoạt động trong ứng dụng có thể hiển thị thông tin nhóm. Khi người dùng nhấp vào nhãn nhóm trong ứng dụng danh bạ của thiết bị, giao diện người dùng cho hoạt động này sẽ xuất hiện.
viewGroupActionLabel
Nhãn mà ứng dụng danh bạ hiển thị cho một thành phần điều khiển trê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 ảnh trong mục luồng cho một liên hệ thô.

Phần tử <ContactsDataKind>

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

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

có trong:

<ContactsAccountType>

Nội dung mô tả:

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

Thuộc tính:

android:mimeType
Loại MIME tuỳ chỉnh mà bạn đã xác định cho một trong các loại hàng dữ liệu tuỳ chỉnh trong bảng ContactsContract.Data. Ví dụ: giá trị vnd.android.cursor.item/vnd.example.locationstatus có thể là loại MIME tuỳ chỉnh cho một hàng dữ liệu ghi lại vị trí đã biết gần đây nhất của người liên hệ.
android:icon
Tài nguyên drawable 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 sử dụng thuộc tính này để cho người dùng biết rằng dữ liệu đến từ dịch vụ của bạn.
android:summaryColumn
Tên cột cho giá trị đầu tiên trong hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này được 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 phầ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 hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này được hiển thị dưới dạng dòng thứ hai của mục nhập cho hàng dữ liệu này. Xem thêm android:summaryColumn.

Các tính năng khác 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, Trì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 liên hệ
  • Tính năng cho ảnh

Nhóm liên hệ

Trình cung cấp danh bạ có thể tuỳ ý gắn nhãn cho các tập hợp người liên hệ có liên quan bằng dữ liệu nhóm. Nếu máy chủ liên kết với một tài khoản người dùng muốn duy trì các nhóm, thì bộ điều hợp đồng bộ hoá cho loại tài khoản của tài khoản đó phải chuyển dữ liệu nhóm giữa Trình cung cấp danh bạ và máy chủ. Khi người dùng thêm một người liên hệ mới vào máy chủ rồi đưa người liên hệ này vào một nhóm mới, bộ điều hợp đồng bộ hoá phải thêm nhóm mới vào bảng ContactsContract.Groups. 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 đang thiết kế một 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 không sử dụng nhóm, thì bạn cần yêu cầu Trình cung cấp hiển thị dữ liệu của bạn. Trong mã được thực thi khi người dùng thêm tài khoản vào thiết bị, hãy cập nhật hàng ContactsContract.Settings mà Trình cung cấp danh bạ thêm cho tài khoản. Trong hàng này, hãy đặt giá trị của cột Settings.UNGROUPED_VISIBLE thành 1. Khi bạn làm việc này, Trình cung cấp danh bạ sẽ luôn hiển thị dữ liệu danh bạ của bạn, ngay cả khi bạn không sử dụng nhóm.

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

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

Tài liệu tham khảo cho ContactsContract.Contacts.Photo và ContactsContract.RawContacts.DisplayPhoto có các ví dụ về cách truy xuất thông tin ả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 _ID của người liên hệ thô, Photo.CONTENT_ITEM_TYPE và cột IS_PRIMARY để tìm hàng ảnh chính của người liên hệ thô.

Dữ liệu luồng xã hội của một người cũng có thể bao gồm ảnh. Những ả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 trên mạng xã hội.