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 dữ liệu về con người của thiết bị. Trình cung cấp danh bạ là nguồn dữ liệu bạn thấy trong ứng dụng danh bạ của thiết bị. Bạn cũng có thể truy cập 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 từng người dùng, do đó khiến tổ chức của nhà cung cấp trở nên phức tạp. Do đó, API của 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 để 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 điều 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 ứng dụng nhà cung cấp.
  • Cách ghi bộ điều hợp đồng bộ hoá để đồng bộ hoá dữ liệu từ máy chủ của bạn vào Trình cung cấp danh bạ.

Hướng dẫn này giả định rằng bạn đã nắm được kiến thức cơ bản về trình cung cấp nội dung trên 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. Lớp này lưu giữ 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 ứng dụng cung cấp, như minh hoạ trong hình 1:

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

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

ContactsContract.Contacts bảng
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 hàng liên hệ thô.
ContactsContract.RawContacts bảng
Các hàng chứa nội dung 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.
ContactsContract.Data bảng
Hàng chứa thông tin chi tiết về thông tin liên hệ chưa qua xử lý, 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 hợp đồng trong ContactsContract là bảng phụ mà Trình cung cấp danh bạ dùng để quản lý các hoạt động hoặc hỗ trợ các chức năng cụ thể trong danh bạ hoặc ứng dụng điện thoại của thiết bị.

Danh bạ thô

Địa chỉ liên hệ thô biểu thị dữ liệu của một người đến từ một loại tài khoản và tên tài khoản duy nhất. Vì Trình cung cấp danh bạ cho phép nhiều dịch vụ trực tuyến làm nguồn dữ liệu cho một người, nên Trình cung cấp danh bạ cho phép có nhiều địa chỉ liên hệ thô cho cùng một người. Khi có nhiều địa chỉ liên hệ thô, người dùng cũng có thể 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 về địa chỉ liên hệ thô không được lưu trữ trong bảng ContactsContract.RawContacts. Thay vào đó, dữ liệu này được lưu trữ trong một hoặc nhiều hàng trong bảng ContactsContract.Data. Mỗi hàng dữ liệu có một cột Data.RAW_CONTACT_ID chứa giá trị RawContacts._ID của hàng mẹ ContactsContract.RawContacts.

Cột địa chỉ liên hệ thô quan trọng

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

Bảng 1. Cột địa chỉ liên hệ thô quan trọng.

Tên cột Mục đích sử dụng Ghi chú
ACCOUNT_NAME Tên tài khoản cho loại tài khoản là nguồn của địa chỉ liên hệ thô này. Ví dụ: tên tài khoản Google là một trong những địa chỉ Gmail của chủ sở hữu thiết bị. Xem mục tiếp theo dành cho ACCOUNT_TYPE để biết thêm thông tin. Định dạng của tên này dành riêng cho loại tài khoản của tài khoản. Địa chỉ email này không nhất thiết là một địa chỉ email.
ACCOUNT_TYPE Loại tài khoản là nguồn của địa chỉ liên hệ thô này. Ví dụ: loại tài khoản của Tài khoản Google là com.google. Hãy luôn đáp ứng điều kiện về loại tài khoản của bạn bằng giá trị nhận dạng miền dành cho miền mà bạn sở hữu hoặc kiểm soát. Việc này giúp đả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ó 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 địa chỉ 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ộ chuyển đổi đồng bộ hoá có thể xoá hàng đó khỏi máy chủ của họ và cuối cùng là xoá hàng đó khỏi kho lưu trữ.

Ghi chú

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

  • Tên của địa chỉ liên hệ thô không được lưu trữ trong hàng 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. Địa chỉ 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 bạn trong hàng địa chỉ liên hệ thô, trước tiên, bạn phải đăng ký dữ liệu đó bằng AccountManager. Để thực hiện việc này, hãy nhắc người dùng thêm loại tài khoản và tên tài khoản của họ vào danh sách tài khoản. Nếu bạn không thực hiện thao tác này, Trình cung cấp danh bạ sẽ tự động xoá hàng địa chỉ liên hệ chưa qua xử lý của bạn.

    Ví dụ: nếu bạn muốn ứng dụng duy trì dữ liệu danh bạ cho dịch vụ dựa trên web bằng 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 "type" (com.example.dataservice) và "name" của tài khoản (becky.smart@dataservice.example.com) trước khi ứng dụng có thể thêm các hàng liên hệ chưa qua xử lý. Bạn có thể giải thích yêu cầu này cho người dùng trong tài liệu hoặc có thể nhắc người dùng thêm loại và tên, hoặc cả hai. Các loại tài khoản và tên tài khoản sẽ được mô tả chi tiết hơn trong phần tiếp theo.

Nguồn dữ liệu danh bạ chưa qua xử lý

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

  • 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 Accounts (Tài khoản).

Giả sử Emily Digginson 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ạ và thêm "Thomas Higginson". Sau đó, cô đăng nhập vào Gmail bằng tài khoản emilyd@gmail.com rồi gửi email cho "Thomas Higginson" để tự động thêm người này vào danh bạ. Cô cũng theo dõi "colonel_tom" (mã đăng ký trên Twitter của Thomas Higginson) trên Twitter.

Trình cung cấp danh bạ tạo ra ba địa chỉ liên hệ thô là kết quả của công việc này:

  1. Địa chỉ liên hệ chưa qua xử lý của "Thomas Higginson" có liên quan đến emily.dickinson@gmail.com. Loại tài khoản người dùng là Google.
  2. Địa chỉ liên hệ thô thứ hai của "Thomas Higginson" liên quan đến emilyd@gmail.com. Loại tài khoản người dùng cũng là Google. Có một địa chỉ liên hệ thô thứ hai mặc dù tên giống với tên trước đó, vì người đó đã được thêm vào một tài khoản người dùng khác.
  3. Địa chỉ liên hệ thô thứ ba của "Thomas Higginson" liên quan đến "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 về địa chỉ liên hệ thô được lưu trữ trong hàng ContactsContract.Data được liên kết với giá trị _ID của địa chỉ liên hệ thô. Điều này cho phép một địa chỉ liên hệ thô có nhiều bản sao của cùng một loại dữ liệu 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ô của Thomas Higginson liên kết với Tài khoản Google emilyd@gmail.com) có địa chỉ email nhà riêng của 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 với địa chỉ liên hệ thô này.

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

Tên cột mô tả

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

RAW_CONTACT_ID
Giá trị cột _ID của địa chỉ liên hệ thô của 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 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ể dùng trong mọi ứng dụng hoặc bộ điều hợp đồng bộ hoá hoạt động với Trình cung cấp danh bạ.
IS_PRIMARY
Nếu loại hàng dữ liệu này có thể xuất hiện nhiều lần cho một địa chỉ 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 mục liên hệ rồi chọn Đặt giá trị 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ó sẵn 15 cột chung có tên từ DATA1 đến DATA15 và thêm 4 cột chung từ SYNC1 đến SYNC4 chỉ nên dùng trong các bộ chuyển đổi đồng bộ hoá. Các hằng số tên cột chung luôn hoạt động, bất kể loại dữ liệu mà hàng đó chứa.

Cột DATA1 đã được lập chỉ mục. Trình cung cấp danh bạ luôn sử dụng cột này cho dữ liệu mà nhà cung cấp dự kiến sẽ là mục tiêu thường xuyên nhất của truy vấn. Ví dụ: trong 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ùng để lưu trữ dữ liệu Đối tượng lớn nhị phân (BLOB) chẳng hạn như hình thu nhỏ của ảnh.

Tên cột theo loại cụ thể

Để hỗ trợ thao tác với 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 hằng số tên cột theo từng loại, được xác định trong các lớp con của ContactsContract.CommonDataKinds. Các hằng số chỉ đặt một tên hằng số khác cho cùng một tên cột. Điều này giúp bạn truy cập vào dữ liệu trong một hàng thuộc một loại cụ thể.

Ví dụ: lớp ContactsContract.CommonDataKinds.Email xác định hằng số tên cột theo kiểu 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: Khô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 hàng có một trong các loại MIME được xác định trước của trình cung cấp. Nếu làm như vậy, bạn có thể mất dữ liệu hoặc khiến trình cung cấp gặp sự cố. Ví dụ: bạn không nên thêm 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, bạn có thể tuỳ ý xác định tên cột dành riêng cho từng loại và sử dụng các cột theo ý muốn.

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

Cách tên cột theo kiểu 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 kiểu cụ thể thường được sử 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 địa chỉ liên hệ thô được liên kết với hàng dữ liệu này. Địa chỉ liên hệ thô chỉ có một trong các hàng này.
ContactsContract.CommonDataKinds.Photo Ảnh chính của người liên hệ chưa qua xử lý được liên kết với hàng dữ liệu này. Địa chỉ liên hệ thô chỉ có một trong các hàng này.
ContactsContract.CommonDataKinds.Email Địa chỉ email của người liên hệ chưa qua xử lý được liên kết với hàng dữ liệu này. Một địa chỉ liên hệ thô có thể có nhiều địa chỉ email.
ContactsContract.CommonDataKinds.StructuredPostal Địa chỉ bưu chính cho người liên hệ chưa qua xử lý được liên kết với hàng dữ liệu này. Địa chỉ liên hệ thô có thể có nhiều địa chỉ bưu điện.
ContactsContract.CommonDataKinds.GroupMembership Giá trị nhận dạng liên kết địa chỉ 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 khi sử dụng loại tài khoản và tên tài khoản. Họ được mô tả chi tiết hơn trong phần Nhóm liên hệ.

Danh bạ

Trình cung cấp danh bạ kết hợp các hàng thông tin liên hệ chưa qua xử lý 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 thông tin liên hệ. Điều này tạo điều kiện cho việc hiển thị và sửa đổi tất cả dữ liệu mà một 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 danh bạ thô với hàng liên hệ hiện có. Cả ứng dụng lẫn bộ điều hợp đồng bộ hóa đều không được phép thêm danh bạ và một số cột trong hàng danh bạ ở chế độ chỉ đọc.

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

Trình cung cấp danh bạ tạo một địa chỉ liên hệ mới để phản hồi việc thêm một địa chỉ liên hệ chưa qua xử lý mới không khớp với bất kỳ địa chỉ liên hệ nào hiện có. Trình cung cấp cũng thực hiện việc này nếu dữ liệu của một mục liên hệ chưa qua xử lý thay đổi theo cách không còn khớp với mục liên hệ đã được đính kèm trước đó. Nếu ứng dụng hoặc bộ chuyển đổi đồng bộ hoá tạo một địa chỉ liên hệ thô mới trùng khớp với một địa chỉ liên hệ hiện có, thì địa chỉ liên hệ thô mới sẽ được tổng hợp vào địa chỉ 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 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 danh bạ thô ContactsContract.RawContacts chứa giá trị _ID cho hàng danh bạ liên kết với từng hàng danh bạ thô.

Bảng ContactsContract.Contacts cũng có cột LOOKUP_KEY là đường liên kết "vĩnh viễn" với hàng người liên hệ. Vì Trình cung cấp danh bạ tự động duy trì danh bạ nên có thể thay đổi giá trị _ID của hàng danh bạ để phản hồi một quá trình 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 mục liên hệ vẫn sẽ trỏ đến hàng mục liên hệ, vì vậy, bạn có thể sử dụng LOOKUP_KEY để duy trì đường liên kết đến mục 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 liên hệ giữa 3 bảng chính.

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

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

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

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

Nếu ứng dụng của bạn đang truy cập hoặc cập nhật những trường hoặc API này, hãy sử dụng các phương thức thay thế. Ví dụ: bạn có thể thực hiện một số trường hợp sử dụng nhất định bằng cách sử dụng trình cung cấp nội dung riêng tư hoặc dữ liệu khác 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 chịu ảnh hưởng của thay đổi này, bạn có thể xoá các trường dữ liệu này theo cách thủ công. Để thực hiện việc này, hãy chạy lệnh ADB sau đây trên một thiết bị chạy Android 4.1 (API cấp 16) trở lên:

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

Dữ liệu qua bộ chuyển đổi đồ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 chuyển vào Trình cung cấp danh bạ từ các dịch vụ web thông qua bộ chuyển đổi đồng bộ hoá. Bộ chuyển đổi này sẽ tự động chuyển dữ liệu giữa thiết bị và các dịch vụ. Bộ chuyển đổi đồng bộ hoá chạy ở chế độ nền dưới sự kiểm soát của hệ thống và gọi các phương thức ContentResolver để quản lý dữ liệu.

Trong Android, dịch vụ web mà bộ điều hợp đồng bộ hoá hoạt động được xác định theo một 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 tài khoản đó. Các loại tài khoản và tên tài khoản được mô tả ngắn gọn trong phần Nguồn của dữ liệu địa chỉ liên hệ chưa qua xử lý. Các định nghĩa sau đây cung cấp thêm thông tin chi tiết, đồng thời mô tả mối liên hệ giữa loại và tên tài khoản với bộ chuyển đổi và dịch vụ đồng bộ hoá.

Loại tài khoản
Xác định một dịch vụ mà người dùng đã lưu trữ dữ liệu. Trong hầu hết trường hợp, người dùng phải xác thực dịch vụ. Ví dụ: Danh bạ Google là một loại tài khoản và đượ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 cũng giống như Tài khoản Google, có địa chỉ email làm tên tài khoản. Các dịch vụ khác có thể sử dụng tên người dùng hoặc mã nhận dạng dạng số chỉ gồm một từ.

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 danh bạ khác cho công việc. Tên tài khoản thường là duy nhất. Cả hai sẽ cùng xác định một luồng dữ liệu cụ thể giữa Trình cung cấp danh bạ và một dịch vụ bên ngoài.

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

Hình 4 cho thấy cách Trình cung cấp danh bạ tham gia luồng dữ liệu về mọi người. Trong hộp được đánh dấu "bộ điều hợp đồng bộ hóa", 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ề con người

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

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

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

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

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

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

Hồ sơ người dùng

Bảng ContactsContract.Contacts có một hàng duy nhất chứa dữ liệu hồ sơ của người dùng thiết bị. Dữ liệu này mô tả user của thiết bị chứ không phải một trong những danh bạ của người dùng. Hàng địa chỉ liên hệ trong hồ sơ được liên kết với một hàng địa chỉ liên hệ thô cho mỗi hệ thống sử dụng hồ sơ. Mỗi hàng địa chỉ 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.

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

Hãy nhớ rằng bạn nên coi hồ sơ của người dùng là 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 người liên hệ chứa hồ sơ của người dùng, hãy gọi ContentResolver.query(). Đặt URI nội dung thành CONTENT_URI và không cung cấp tiêu chí lựa chọn nào. Bạn cũng có thể sử dụng URI nội dung này làm URI cơ sở để truy xuất dữ liệu hoặc danh bạ chưa qua xử lý cho hồ sơ. Ví dụ: đoạn mã này truy xuất dữ liệu cho cấu hình:

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 mục liên hệ và muốn xác định xem một trong các hàng đó có phải là hồ sơ người dùng hay không, hãy kiểm thử cột IS_USER_PROFILE của hàng đó. Cột này được đặt thành "1" nếu mục 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 giúp theo dõi trạng thái của dữ liệu danh bạ trong kho lưu trữ. Siêu dữ liệu này về kho lưu trữ được lưu trữ ở nhiều nơi, bao gồm các hàng trong bảng Danh bạ thô, Dữ liệu và Danh bạ, bảng ContactsContract.Settings và bảng ContactsContract.SyncState. Bảng sau đây cho thấy tác động của từng phần siêu dữ liệu sau:

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

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

Các bộ chuyển đổi đồng bộ hoá sửa đổi bảng dữ liệu hoặc thông tin liên hệ thô phải luôn thêm chuỗi CALLER_IS_SYNCADAPTER vào URI nội dung mà các bộ chuyển đổi đó sử dụng. Việc này ngăn trình cung cấp đánh dấu các hàng là sửa đổi. Nếu không, nội dung sửa đổi bộ chuyển đổi đồng bộ hoá có vẻ như là nội dung sửa đổi cục bộ và được gửi đến máy chủ, mặc dù máy chủ là nguồn gây ra nội dung sửa đổi.

"1" – đã thay đổi kể từ lần đồng bộ hoá gần 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 được thay đổi.
ContactsContract.RawContacts SOURCE_ID Một giá trị chuỗi xác định duy nhất lượt liên hệ thô này cho tài khoản mà lượt liên hệ được tạo. Khi bộ điều hợp đồng bộ hoá tạo một địa chỉ 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 địa chỉ liên hệ thô mới. Khi ứng dụng Android tạo một địa chỉ liên hệ thô mới, ứng dụng nên để trống cột này. Điều này sẽ báo hiệu cho bộ chuyển đổi đồng bộ hoá rằng cần tạo một địa chỉ 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 từng loại tài khoản và phải ổn định trên quá trình đồng bộ hoá:

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

Quyền truy cập 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:

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

Việc thực hiện sửa đổi từ bộ điều hợp đồng bộ hóa cũng được đề cập chi tiết hơn trong phần Bộ điều hợp đồng bộ hóa nhà cung cấp danh bạ.

Truy vấn thực thể

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

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

Các thực thể giúp đơn giản hoá việc truy vấn. Khi sử dụng một thực thể, bạn có thể truy xuất toàn bộ dữ liệu danh bạ của một người liên hệ hoặc người liên hệ chưa qua xử lý cùng một lúc, thay vì phải truy vấn bảng mẹ trước để lấy mã nhận dạng, sau đó phải truy vấn bảng con bằng mã nhận dạng đó. Ngoài ra, Trình cung cấp danh bạ xử lý truy vấn đối với một thực thể trong một giao dịch duy nhất, giúp đả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 xử lý một tên cột không nằm trong danh sách hằng số tên cột của thực thể đó, bạn sẽ nhận được một Exception.

Đoạn mã sau đây cho biết cách truy xuất tất cả hàng địa chỉ liên hệ chưa qua xử lý của một địa chỉ liên hệ. Đoạn mã là một phần của một ứng dụng lớn hơn có hai hoạt động: "chính" và "chi tiết". Hoạt động chính hiển thị danh sách các hàng liên hệ; khi người dùng chọn một hàng, hoạt động sẽ gửi mã nhận dạng của mình đế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ừ mọi địa chỉ liên hệ chưa qua xử lý được liên kết với địa chỉ liên hệ đã chọn.

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

Kotlin

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

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

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

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

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

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

Java

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

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

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

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

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

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

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

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

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ế độ lô", 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 applyBatch() trong một giao dịch duy nhất, nên các nội dung sửa đổi của bạn sẽ không bao giờ khiến kho lưu trữ danh bạ ở trạng thái không nhất quán. Việc sửa đổi hàng loạt cũng tạo điều kiện thuận lợi cho việc chèn cùng lúc thông tin liên hệ thô và dữ liệu chi tiết của thông tin liên hệ đó.

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ý nội dung sửa đổi trong ứng dụng. Thao tác này được mô tả chi tiết hơn trong phần Truy xuất và sửa đổi bằng ý định.

Điểm lợi nhuận

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

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

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

Tham chiếu trở lại khi sửa đổi

Khi chèn một hàng địa chỉ liên hệ thô mới và các hàng dữ liệu được liên kết dưới dạng một tập hợp đối tượng ContentProviderOperation, bạn phải liên kết các hàng dữ liệu với hàng địa chỉ liên hệ thô bằng cách chèn giá trị _ID của địa chỉ liên hệ thô làm giá trị RAW_CONTACT_ID. Tuy nhiên, giá trị này sẽ không xuất hiệ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 địa chỉ 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 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 các đối tượng ContentProviderResult từ applyBatch(). Khi các thao tác hàng loạt được áp dụng, kết quả của mỗi thao tác sẽ được lưu trữ trong một mảng kết quả trung gian. Giá trị previousResult là chỉ mục của một trong những kết quả này. 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 "tham chiếu lại" cho 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 với kích thước của ArrayList trong số các đối tượng ContentProviderOperation mà bạn cung cấp. Tuy nhiên, tất cả các phần tử trong mảng kết quả đều được đặt thành null và nếu bạn cố gắng tham chiếu lại đến một kết quả của một thao tác chưa được áp dụng, thì withValueBackReference() sẽ gửi một Exception.

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

Đoạn mã đầu tiên truy xuất dữ liệu người liên hệ qua giao diện người dùng. Đến đây, người dùng đã chọn tài khoản để thêm địa chỉ liên hệ chưa qua xử lý mới.

Kotlin

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

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

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

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

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

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

Kotlin

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

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

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

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

Java

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

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

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

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

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

Mỗi đối tượng trình tạo thao tác 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 sẽ thêm hàng liên hệ thô và trả về giá trị _ID mới. Do đó, mỗi hàng dữ liệu sẽ được RAW_CONTACT_ID tự động liên kết với hàng ContactsContract.RawContacts mới chứa hàng đó.

Đối tượng ContentProviderOperation.Builder thêm hàng email sẽ được gắn cờ bằng withYieldAllowed() để đặt điểm lợi nhuận:

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à công cụ liên hệ chưa qua xử lý 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 tối ưu, một phương thức áp dụng các giao dịch sửa đổi mà không cần phải 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 và sau đó kiểm tra những nội dung sửa đổi khác có thể đã được thực hiện cùng lúc. Nếu thấy có nội dung sửa đổi không nhất quán, hãy khôi phục giao dịch của mình, rồi thử lại.

Tính năng kiểm soát đồng thời tối ưu rất hữu ích cho thiết bị di động vì chỉ có một người dùng tại một thời điểm và hiếm khi có quyền truy cập đồng thời vào kho lưu trữ dữ liệu. Vì không sử dụng 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 mở khoá.

Để sử dụng chế độ kiểm soát đồng thời tối ưu 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 địa chỉ 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 bằng cách sử dụng phương thức newAssertQuery(Uri). Đối với URI nội dung, hãy sử dụng RawContacts.CONTENT_URI với _ID của địa chỉ liên hệ thô được thêm vào đó.
  3. Đối với đối tượng ContentProviderOperation.Builder, hãy gọi withValue() để so sánh cột VERSION với số phiên bản bạn vừa truy xuất.
  4. Đối với cùng một ContentProviderOperation.Builder, hãy gọi withExpectedCount() để đảm bảo rằng câu nhận định này chỉ kiểm thử một hàng.
  5. Hãy 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 vào applyBatch().
  6. Áp dụng giao dịch theo lô.

Nếu hàng địa chỉ liên hệ thô được cập nhật bằng một thao tác khác trong khoảng thời gian từ khi bạn đọc hàng đến thời điểm 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ộ loạt thao tác sẽ được sao lưu. Sau đó, bạn có thể chọn thử lại lô hoặc thực hiện một số thao tác khác.

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

Kotlin

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

...

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

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

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

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

ops.add(assertOp.build())

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

...

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

Java

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

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

...

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

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

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

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

ops.add(assertOp.build());

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

...

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

    } catch (OperationApplicationException e) {

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

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

Việc gửi ý định đến ứng dụng danh bạ của thiết bị sẽ cho phép bạn truy cập gián tiếp vào Trình cung cấp danh bạ. Ý định sẽ khởi động giao diện người dùng của ứng dụng danh bạ của 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à đưa người liên hệ đó trở lại ứng dụng để thực hiện thêm công việc.
  • Chỉnh sửa dữ liệu của một mục liên hệ hiện có.
  • Chèn địa chỉ liên hệ thô mới cho bất kỳ tài khoản nào của họ.
  • Xoá dữ liệu về người liên hệ hoặc danh bạ.

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

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

Quy trình chung để gửi ý định truy cập vào trình cung cấp được mô tả chi tiết trong hướng dẫn Kiến thức cơ bản về Trình cung cấp nội dung trong phần "Quyền truy cập dữ liệu thông qua ý định". Thao tác, loại MIME và giá trị dữ liệu mà bạn 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 bạn có thể sử dụng với putExtra() được liệt kê ở tài liệu tham khảo về ContactsContract.Intents.Insert:

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

Việc cần làm 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:
  • Contacts.CONTENT_URI – hiển thị danh sách người liên hệ.
  • Phone.CONTENT_URI – hiển thị danh sách số điện thoại của một người liên hệ chưa qua xử lý.
  • StructuredPostal.CONTENT_URI: hiển thị danh sách địa chỉ bưu chính của một địa chỉ liên hệ chưa qua xử lý.
  • Email.CONTENT_URI – hiển thị danh sách địa chỉ email của một địa chỉ liên hệ chưa qua xử lý.
Không được sử dụng Cho thấy danh sách địa chỉ liên hệ chưa qua xử lý hoặc danh sách dữ liệu của địa chỉ liên hệ chưa qua xử lý, 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 thức của URI là URI nội dung của bảng với LOOKUP_ID của hàng được thêm vào đó. Ứng dụng danh bạ của thiết bị uỷ quyền quyền đọc và ghi đối với URI nội dung này trong suốt vòng đời của hoạt động. Xem hướng dẫn Kiến thức cơ bản về Trình cung cấp nội dung để biết thêm thông tin chi tiết.

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

Lưu ý: 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 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, ứng dụng danh bạ sẽ hiển thị tên mà bạn gửi và 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á thông tin liên hệ chưa qua xử lý hoặc bất kỳ dữ liệu nào có ý định. Thay vào đó, để xoá một địa chỉ 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 người liên hệ và dữ liệu chưa qua xử lý 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à mới nhất, nên Trình cung cấp danh bạ có các quy tắc rõ ràng về tính toàn vẹn của dữ liệu. Bạn có trách nhiệm tuân thủ các quy tắc này khi sửa đổi dữ liệu danh bạ. Sau đây là các quy tắc quan trọng:

Luôn thêm một hàng ContactsContract.CommonDataKinds.StructuredName cho mỗi hàng ContactsContract.RawContacts mà bạn thêm vào.
Hàng ContactsContract.RawContacts không có hàng ContactsContract.CommonDataKinds.StructuredName trong bảng ContactsContract.Data có thể gây ra sự cố trong quá trình tổng hợp.
Luôn liên kết các hàng ContactsContract.Data mới với hàng ContactsContract.RawContacts gốc.
Hàng ContactsContract.Data không liên kết với ContactsContract.RawContacts sẽ không hiển thị 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 đối với các địa chỉ liên hệ chưa qua xử lý 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. Bạn cần đảm bảo rằng ứng dụng của bạn chỉ sửa đổi hoặc xoá dữ liệu cho các hàng thuộc về bạn và ứng dụng chỉ chèn dữ liệu có loại tài khoản và tên do 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 lớp này cho các đơn vị quản lý, URI nội dung, đường dẫn URI, tên cột, loại MIME và giá trị TYPE.
Việc sử dụng các hằng số này sẽ giúp bạn tránh được lỗi. Bạn cũng sẽ nhận được thông báo về cảnh báo của trình biên dịch nếu có bất kỳ hằng số nào không 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 từng loại cụ thể với tên cột mặc định. Trong ứng dụng danh bạ của thiết bị, dữ liệu về các hàng sẽ xuất hiện nhưng không thể chỉnh sửa hoặc xoá và 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 hoạt động của người chỉnh sửa trong ứng dụng của riêng mình.

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

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

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

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

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

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

Để 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 nhà cung cấp nội dung và dịch vụ nhưng có thể xử lý nhiều tên tài khoản cho cùng một dịch vụ. Khung này cũng cho phép nhiều bộ điều hợp đồng bộ hoá cho cùng một dịch vụ và nhà cung cấp.

Đồng bộ hoá các lớp và tệp bộ chuyển đổi

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

Lưu ý: Việc sử dụng loại tài khoản để nhận dạng bộ điều hợp đồng bộ hoá sẽ 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ộ chuyển đổi đồng bộ hoá cho các dịch vụ trực tuyến của Google đều có cùng một loại tài khoản com.google. Khi người dùng thêm Tài khoản Google vào thiết bị của họ, 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 sẽ cung cấp một khung xác thực tương tự và thường được dùng kết hợp với khung bộ điều hợp đồng bộ hoá. Khung xác thực sử dụng trình xác thực trình bổ trợ là lớp con của AbstractAccountAuthenticator. Trình xác thực 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 các thông tin tương tự của người dùng (thông tin đăng nhập của người dùng).
  2. Gửi thông tin xác thực đến dịch vụ
  3. Kiểm tra câu trả lời của dịch vụ.

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

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

Triển khai bộ chuyển đổi đồng bộ hoá

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

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

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

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

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

Các mục trong luồng luôn được liên kết với một địa chỉ liên hệ chưa qua xử lý. android.provider.ContactsContract.StreamItemsColumn#RAW_CONTACT_ID liên kết đến giá trị _ID của địa chỉ liên hệ thô. Loại tài khoản và tên tài khoản của địa chỉ liên hệ chưa qua xử lý cũng được lưu trữ trong hàng mục trong luồng phát.

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

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

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

Ảnh trong luồng mạng xã hội

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

android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
Bản trình bày 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ó khả năng tương thích ngược với các phiên bản trước của Trình cung cấp danh bạ 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.StreamItemPhotosColumn#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 một hình thu nhỏ của ảnh để đọc.
android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_FILE_ID
Giá trị nhận dạng dạng số của ảnh cho một người liên hệ chưa qua xử lý. Thêm giá trị này vào hằng số DisplayPhoto.CONTENT_URI để có URI nội dung trỏ đến một tệp ảnh duy nhất, sau đó gọi openAssetFileDescriptor() để xử lý tệp ảnh đó.
android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_URI
URI nội dung trỏ trực tiếp đến tệp ảnh cho ảnh được biểu thị bằng hàng này. Gọi openAssetFileDescriptor() bằng URI này để xử lý tệp ảnh.

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

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

  • Các bảng này yêu cầu thêm quyền truy cập. Để đọc những nội dung 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 quyền đó, ứng dụng của bạn phải có quyền android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • Đối với bảng android.provider.ContactsContract.StreamItems, số lượng hàng được lưu trữ cho mỗi địa chỉ liên hệ thô là có giới hạn. Sau 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ới trong mục trong luồng bằng cách tự động xoá các hàng có android.provider.ContactsContract.StreamItemsColumn#TIMESTAMP. Để đạt giới hạn này, hãy phát một truy vấn đến URI nội dung android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Bạn có thể để lại mọi đối số khác vớ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 một cột android.provider.ContactsContract.StreamItems#MAX_ITEMS.

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

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

Dữ liệu luồng trên mạng xã hội do Trình cung cấp danh bạ quản lý, kết hợp với ứng dụng danh bạ của thiết bị, mang đến một cách hiệu quả để kết nối hệ thống mạng xã hội của bạn với danh bạ hiện có. 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 của bạn với Trình cung cấp danh bạ bằng 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 bảng android.provider.ContactsContract.StreamItems và android.provider.ContactsContract.StreamItemPhotos để sử dụng sau này.
  • Ngoài việc đồng bộ hoá thông thường, bạn có thể kích hoạt bộ điều hợp đồng bộ hoá để truy xuất thêm dữ liệu khi người dùng chọn một mục liên hệ để xem. Việc 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 mới nhất trên luồng 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 ý định khi có người 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ệ đó qua 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 thực hiện đồng bộ hoá toàn bộ bằng bộ điều hợp đồng bộ hoá.
  • Người dùng có thể thêm một người liên hệ vào dịch vụ mạng xã hội của bạn trong khi xem người liên hệ đó trong ứng dụng danh bạ của thiết bị. Bạn bật tính năng này bằng tính năng "mời liên hệ" mà bạn bật bằng cách kết hợp một hoạt động thêm một người liên hệ hiện có vào mạng của bạn và tệp XML cung cấp ứng dụng danh bạ của thiết bị cũng như Trình cung cấp danh bạ cùng với thông tin chi tiết về ứng dụng của bạn.

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

Đăng ký xử lý lượt xem trên mạng xã hội

Cách đăng ký bộ điều hợp đồng bộ hóa để nhận thông báo khi người dùng xem một mục liên hệ do bộ điều hợp đồng bộ hóa quản lý:

  1. Tạo một tệp có tên 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ụ nhận được thông báo khi người dùng mở trang chi tiết về một mục liên hệ trong ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính viewContactNotifyService="serviceclass" vào phần tử, trong đó serviceclass là tên lớp đủ điều kiện của dịch vụ sẽ nhận được ý định từ ứng dụng danh bạ của thiết bị. Đối với dịch vụ thông báo, hãy sử dụng lớp mở rộng IntentService để cho phép dịch vụ nhận ý định. Dữ liệu trong ý định đến chứa URI nội dung của địa chỉ liên hệ chưa qua xử lý 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 với rồi gọi bộ điều hợp đồng bộ hoá để cập nhật dữ liệu cho địa chỉ liên hệ thô.

Để đăng ký một hoạt động được gọi khi người dùng nhấp vào một ảnh hoặc mục trên luồng hoặc cả hai, hãy làm như sau:

  1. Tạo một tệp có tên 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 nhằm xử lý việc người dùng nhấp vào một mục trên 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 nhằm xử lý 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 <ContactsAccountType>.

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

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

Người dùng không phải rời khỏi ứng dụng danh bạ của thiết bị để mời người liên hệ truy cập vào trang web 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 ý đị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 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 Add Connection (Thêm kết nối) trong ứng dụng danh bạ của thiết bị.

Lưu ý: ContactsSource là tên thẻ không còn được dùng 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 sự tương tác của ứng dụng và bộ điều hợp đồng bộ hoá với ứng dụng danh bạ và Trình cung cấp danh bạ. Những phần tử này được mô tả trong những phần sau.

Phần tử <ContactsAccountType>

Phần tử <ContactsAccountType> kiểm soát sự tương tác giữa ứng dụng của bạn với ứng dụng danh bạ. Hàm này có cú pháp sau:

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

có trong:

res/xml/contacts.xml

có thể chứa:

<ContactsDataKind>

Description:

Khai báo các thành phần Android và nhãn giao diện người dùng cho phép người dùng mời một trong 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: là 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 Add connection (Thêm đường kết nối). Ví dụ: bạn có thể sử dụng chuỗi "Theo dõi trong mạng của tôi". Bạn có thể sử dụng giá trị nhận dạng tài nguyên chuỗi cho nhãn này.
viewContactNotifyService
Tên lớp đủ điều kiện của một dịch vụ trong ứng dụng và sẽ nhận được thông báo khi người dùng xem một mục 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 cho phép ứng dụng trì hoãn các hoạt động cần nhiều dữ liệu đến khi cần đến. Ví dụ: ứng dụng của bạn có thể phản hồi thông báo này bằng cách đọc và hiển thị ảnh có độ phân giải cao cũng như các mục mới nhất trên mạng xã hội của người liên hệ đó. Tính năng này được mô tả chi tiết hơn trong phần Tương tác luồng trên 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 mà có thể hiển thị thông tin về 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 của hoạt động này sẽ hiển thị.
viewGroupActionLabel
Nhãn mà ứng dụng danh bạ hiển thị để kiểm soát giao diện người dùng, cho phép người dùng xem các nhóm trong ứng dụng của bạn.

Được phép 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 địa chỉ liên hệ thô.
viewStreamItemPhotoActivity
Tên lớp đủ điều kiện của một hoạt động trong ứng dụng mà ứng dụng danh bạ của thiết bị chạy khi người dùng nhấp vào một ảnh trong mục trên luồng cho một liên hệ thô.

Phần tử <ContactsDataData>

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 trên giao diện người dùng của ứng dụng danh bạ. Hàm này có cú pháp sau:

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

có trong:

<ContactsAccountType>

Description:

Hãy 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 địa chỉ liên hệ thô. Mỗi phần tử con <ContactsDataKind> của <ContactsAccountType> đại diện cho một loại hàng dữ liệu tuỳ chỉnh mà bộ chuyển đổi đồng bộ hoá sẽ thêm vào bảng ContactsContract.Data. Thêm một phần tử <ContactsDataKind> cho từng loại MIME tuỳ chỉnh mà bạn sử dụng. Bạn không phải thêm phần tử này nếu có hàng dữ liệu tuỳ chỉnh mà bạn không muốn hiển thị dữ liệu.

Thuộc tính:

android:mimeType
Loại MIME tuỳ chỉnh mà bạn đã xác định cho một trong các loại hàng dữ liệu tuỳ chỉnh trong bảng ContactsContract.Data. Ví dụ: giá trị vnd.android.cursor.item/vnd.example.locationstatus có thể là loại MIME tuỳ chỉnh cho một hàng dữ liệu ghi lại vị trí đã biết mới đây nhất của một liên hệ.
android:icon
Một tài nguyên có thể vẽ trên Android mà ứng dụng danh bạ hiển thị bên cạnh dữ liệu của bạn. 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ị được hiển thị ở dòng đầu tiên của mục nhập cho hàng dữ liệu này. Dòng đầu tiên dự kiến sẽ được sử dụng như phần tóm tắt dữ liệu, nhưng bạn không bắt buộc phải sử dụng dòng này. 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ị được hiển thị ở dòng thứ hai của mục nhập cho hàng dữ liệu này. Hãy xem thêm android:summaryColumn.

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

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

  • Nhóm liên hệ
  • Tính năng của ả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ì nhóm, thì bộ điều hợp đồng bộ hoá cho loại tài khoản của tài khoản đó sẽ chuyển dữ liệu nhóm giữa Trình cung cấp danh bạ và máy chủ. Khi người dùng thêm một mục liên hệ mới vào máy chủ rồi đặt mục 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 hoặc các nhóm có địa chỉ liên hệ thô đượ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á để thêm dữ liệu liên hệ thô từ máy chủ vào Trình cung cấp danh bạ và bạn hiện không sử dụng nhóm, thì bạn cần yêu cầu Nhà cung cấp làm cho dữ liệu của bạn hiển thị. Trong mã được thực thi khi người dùng thêm một tài khoản vào thiết bị, hãy cập nhật hàng ContactsContract.Settings mà 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 là 1. Khi bạn thực hiện 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 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 địa chỉ liên hệ thô chứa địa chỉ liên hệ đó. Lớp ContactsContract.Contacts.Photo xác định một bảng phụ ContactsContract.Contacts chứa thông tin ảnh cho ảnh chính của một mục liên hệ. Đây là ảnh chính của địa chỉ liên hệ thô chính của mục liên hệ đó. Tương tự, lớp ContactsContract.RawContacts.DisplayPhoto xác định một bảng phụ ContactsContract.RawContacts chứa thông tin ảnh cho ảnh chính của một địa chỉ liên hệ thô.

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

Dữ liệu luồng trên mạ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 luồng trên mạng xã hội.