Trình cung cấp danh bạ

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

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

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

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

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

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

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

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

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

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

Danh bạ thô

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

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

Cột liên hệ thô quan trọng

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

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

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

Ghi chú

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

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

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

Nguồn dữ liệu liên hệ thô

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

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

Người dùng này đã bật tuỳ chọn Sync Contacts (Đồng bộ hoá danh bạ) cho cả 3 tài khoản này trong phần cài đặt Accounts (Tài khoản).

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

Trình cung cấp danh bạ tạo ba địa chỉ liên hệ thô nhờ công việc này:

  1. Một liên hệ thô cho "Thomas Higginson" được liên kết với emily.dickinson@gmail.com. Loại tài khoản người dùng là Google.
  2. Một người liên hệ thô thứ hai cho "Thomas Higginson" được liên kết với emilyd@gmail.com. Loại tài khoản người dùng cũng là Google. Tiếp xúc thô thứ hai thậm chí là mặc dù tên này giống với tên trước đó, vì người này đã được thêm cho một tài khoản người dùng khác nhau.
  3. Một thông tin liên hệ thô thứ ba cho "Thomas Higginson" được liên kết với "belle_of_amherst". Loại tài khoản người dùng là Twitter.

Dữ liệu

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

Lưu ý rằng nhiều loại dữ liệu được lưu trữ trong một bảng duy nhất này. Tên hiển thị, tất cả các hàng số điện thoại, email, địa chỉ bưu chính, ảnh và trang web đều có trong Bảng ContactsContract.Data. Để giúp quản lý việc này, Bảng ContactsContract.Data có một số cột có tên mô tả, và những tên 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, trong khi nội dung của cột tên chung có ý nghĩa khác nhau tuỳ thuộc vào loại dữ liệu.

Tên cột mô tả

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

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

Tên cột chung

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

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

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

Tên cột theo loại

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

Ví dụ: lớp ContactsContract.CommonDataKinds.Email xác định các hằng số tên cột theo loại cụ thể cho hàng ContactsContract.Data có loại MIME là 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", tức là giống như tên chung của cột.

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

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

Cách tên cột theo từng loại ánh xạ 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

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

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

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

Danh bạ

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

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

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

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

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

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

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

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

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

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

Những 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ề danh bạ thường xuyên. Xin lưu ý rằng một số trường trong số này chỉ ảnh hưởng đến thứ hạng của danh bạ khi danh bạ thuộc một loại dữ liệu cụ thể.

Nếu ứng dụng của bạn đang truy cập hoặc cập nhật những trường hoặc API này, hãy sử dụng lựa chọn 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 nhà cung cấp nội dung riêng tư hoặc dữ liệu khác được lưu trữ trong ứng dụng hoặc hệ thống phụ trợ của bạn.

Để xác minh rằng thay đổi này không ảnh hưởng đến chức năng của ứng dụng, bạn có thể xoá 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 trên một thiết bị chạy Android 4.1 (API cấp 16) trở lên:

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

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

Người dùng nhập dữ liệu danh bạ trực tiếp vào thiết bị, nhưng dữ liệu cũng chảy vào Nhà cung cấp danh bạ từ các dịch vụ web thông qua trình chuyển đổi đồng bộ hoá. Trình chuyển đổi này tự động hoá việc chuyển dữ liệu giữa thiết bị và các dịch vụ. Bộ điều hợp đồng bộ hoá chạy ở chế độ nền thuộc quyền 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 sẽ được xác định theo loại tài khoản. Mỗi bộ chuyển đổi đồng bộ hoá hoạt động với một loại tài khoản, nhưng có thể hỗ trợ nhiều tên tài khoản cho loại tài khoản đó. Loại tài khoản và tên tài khoản được mô tả ngắn gọn trong phần Nguồn dữ liệu danh bạ thô. Các định nghĩa sau đây cung cấp chi tiết hơn, đồng thời mô tả mối liên hệ giữa loại tài khoản và tên tài khoản với bộ điều hợp 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. Hầu như mọi lúc, người dùng phải xác thực với dịch vụ. Ví dụ: Danh bạ Google là một loại tài khoản, được xác định bằng mã google.com. Giá trị này tương ứng với loại tài khoản mà AccountManager sử dụng.
Tên tài khoản
Xác định một tài khoản hoặc thông tin đăng nhập cụ thể cho một loại tài khoản. Tài khoản Danh bạ Google giống như Tài khoản Google có địa chỉ email dưới dạng tên tài khoản. Các dịch vụ khác có thể sử dụng tên người dùng chỉ có một từ hoặc mã nhận dạng dạng số.

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

Nếu bạn 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 ghi bộ điều hợp đồng bộ hoá riêng. Điều này được mô tả chi tiết hơn trong phần này Bộ điều hợp đồng bộ hoá Nhà cung cấp danh bạ.

Hình 4 cho thấy cách Trình cung cấp danh bạ đưa vào luồng dữ liệu về con người. Trong hộp được đánh dấu "bộ điều hợp đồng bộ hoá", mỗi bộ chuyển đổi sẽ đượ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 những điều sau quyền:

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

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

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

Hồ sơ người dùng

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

Bạn cần có quyền đặc biệt để truy cập vào hồ sơ người dùng. Ngoài READ_CONTACTS và Cần có WRITE_CONTACTS quyền để đọc và ghi, truy cập vào hồ sơ người dùng cần có android.Manifest.permission#READ_PROFILE và Quyền android.Manifest.permission#permission_PROFILE để đọc và ghi, .

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

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

Kotlin

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

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

Java

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

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

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

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

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

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

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

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

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

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

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

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

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

Truy vấn thực thể

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

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

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

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

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

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

Kotlin

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

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

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

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

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

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

Java

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

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

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

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

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

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

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

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

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

Bất cứ khi nào có thể, bạn nên chèn, cập nhật và xoá dữ liệu trong Trình cung cấp danh bạ ở "chế độ hàng loạt" bằng cách tạo ArrayList của các đối tượng ContentProviderOperation và gọi applyBatch(). Bởi 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 trận đấu giao dịch, nội dung sửa đổi của bạn sẽ không bao giờ khiến kho lưu trữ danh bạ không nhất quán trạng thái. Việc sửa đổi hàng loạt cũng tạo điều kiện để chèn thông tin liên hệ thô và dữ liệu chi tiết của liên hệ đó tại .

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

Điểm lợi nhuận

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

Điểm lợi nhuận dẫn đến nhiều giao dịch cho mỗi lệnh gọi đến applyBatch(). Vì thao tác này, bạn nên đặt một điểm lợi nhuận cho thao tác cuối cùng của một tập hợp các hàng liên quan. Ví dụ: bạn nên đặt điểm lợi nhuận cho thao tác cuối cùng trong tập hợp thêm hàng liên hệ thô và các hàng dữ liệu được liên kết của nó hoặc thao tác cuối cùng cho một tập hợp các hàng có liên quan cho một địa chỉ liên hệ.

Điểm năng suất cũng là một đơn vị hoạt động của 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 trả về nào, thì thao tác nguyên tử nhỏ nhất sẽ là toàn bộ lô thao tác. Nếu sử dụng điểm lợi nhuận, bạn sẽ hoạt động từ việ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 phép toán mang tính nguyên tử.

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

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

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

key
Khoá của một cặp khoá-giá trị. Giá trị của đối số này phải là tên của một cột trong bảng mà bạn đang sửa đổi.
previousResult
Chỉ mục dựa trên 0 của một giá trị trong mảng đối tượng ContentProviderResult từ applyBatch(). Như 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 các kết quả này, được truy xuất và lưu trữ bằng key giá trị. 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 bản ghi đó Giá trị _ID, sau đó tạo "tham chiếu lại" vào khi bạn thêm hàng ContactsContract.Data.

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

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

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

Kotlin

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

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

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

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

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

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

Kotlin

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

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

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

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

Java

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

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

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

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

Tiếp theo, mã sẽ tạo các hàng dữ liệu cho 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() để tải RAW_CONTACT_ID. Các điểm tham chiếu quay lại đối tượng ContentProviderResult từ thao tác đầu tiên, thao tác này sẽ thêm hàng liên hệ thô và trả về giá trị _ID mới. Do đó, mỗi hàng dữ liệu sẽ tự động được liên kết bằng RAW_CONTACT_ID của hàng đó với hàng ContactsContract.RawContacts mới mà hàng dữ liệu đó thuộc về.

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

Kotlin

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

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

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

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

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

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

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

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

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

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

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

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

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

Java

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

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

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

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

Java

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

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

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

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

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

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

Các thao tác hàng loạt cũng cho phép bạn triển khai chế độ kiểm soát đồng thời lạc quan, một phương thức áp dụng các giao dịch sửa đổi mà không cần 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, sau đó kiểm tra các nội dung sửa đổi khác có thể được đưa ra cùng một lúc. Nếu thấy có sự sửa đổi không nhất quán, bạn khôi phục giao dịch của bạn rồi thử lại.

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

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

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

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

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

Kotlin

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

...

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

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

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

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

ops.add(assertOp.build())

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

...

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

Java

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

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

...

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

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

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

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

ops.add(assertOp.build());

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

...

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

    } catch (OperationApplicationException e) {

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

Truy xuất và sửa đổi thông qua ý định

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

  • Chọn một người liên hệ trong danh sách và trả về ứng dụng của bạn để tiếp tục công việc.
  • Chỉnh sửa dữ liệu của một mục liên hệ hiện có.
  • Chèn một người liên hệ mới chưa qua sàng lọc cho bất kỳ tài khoản nào của họ.
  • Xoá dữ liệu về một 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, trước tiên, bạn có thể thu thập dữ liệu rồi gửi dưới dạng của ý định.

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

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

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

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

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

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

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

Ứng dụng danh bạ của thiết bị không cho phép bạn xoá một người liên hệ thô hoặc bất kỳ dữ liệu nào của người liên hệ đó bằng một ý định. Thay vào đó, để xoá một đị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 dữ liệu và thông tin liên hệ thô mới:

Kotlin

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

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

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

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

/*
 * Defines the raw contact row
 */

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

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

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

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

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

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

/*
 * Sets up the email data row
 */

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

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

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

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

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

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

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

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

Java

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

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

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

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

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

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

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

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


/*
 * Defines the raw contact row
 */

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

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

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

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

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

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

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

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

/*
 * Sets up the email data row
 */

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

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

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

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

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

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

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

Bởi 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 chính xác và được cập nhật, Trình cung cấp danh bạ có các quy tắc đặt ra 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ạ. Điều quan trọng quy tắc được liệt kê ở đây:

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

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

Bằng cách tạo và sử dụng 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 hàng dữ liệu của riêng bạn trong bảng ContactsContract.Data. Số hàng của bạn bị giới hạn sử dụng cột được xác định trong ContactsContract.DataColumns, mặc dù bạn có thể lập bản đồ của riêng mình tên cột theo từng loại thành tên cột mặc định. Trong ứng dụng danh bạ của thiết bị, dữ liệu cho các hàng của bạn sẽ hiển thị nhưng không thể chỉnh sửa hoặc xoá, đồng thời người dùng không thể thêm dữ liệu bổ sung. Để cho phép người dùng sửa đổi các hàng dữ liệu tuỳ chỉnh, bạn phải cung cấp một hoạt động trình chỉnh sửa trong ứng dụng của riêng mình.

Để hiển thị dữ liệu tuỳ chỉnh của bạn, 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ử của phần tử đó <ContactsDataKind> phần tử con. Điều này được mô tả chi tiết hơn trong phần <ContactsDataKind> element.

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

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

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

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

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

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

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

Lưu ý: Việc sử dụng loại tài khoản trong quá trình 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ộ điều hợp đồng bộ hoá cho các dịch vụ trực tuyến của Google đều có cùng loại tài khoản com.google. Khi người dùng thêm một Tài khoản Google vào thiết bị của họ, tất cả các bộ điều hợp đồng bộ hoá đã cài đặt dành cho các dịch vụ của Google được liệt kê cùng nhau; từng bộ điều hợp đồng bộ hoá đồng bộ hoá với nhà cung cấp nội dung khác trên thiết bị trong danh sách.

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

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

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

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

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

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

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

Dữ liệu về luồng xã hội

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

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

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

Lưu trữ dữ liệu từ luồng trong các cột sau:

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

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

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

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

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

Cột android.provider.ContactsContract.StreamItemPhotos#PHOTO (một 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 dùng được để đảm bảo khả năng tương thích ngược với các phiên bản Danh bạ trước đó Nhà cung cấp đã sử dụng dữ liệu này để lưu trữ ảnh. Tuy nhiên, trong phiên bản hiện tại, bạn không nên sử dụng cột này để lưu trữ ảnh. Thay vào đó, hãy sử dụng android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID hoặc android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (cả hai đều được mô tả trong các điểm sau) để lưu trữ ảnh trong một tệp. Cột này hiện chứa hình thu nhỏ của ảnh có sẵn để đọc.
android.provider.contactsContract.StreamItemPhotosColumn#PHOTO_FILE_ID
Mã nhận dạng dạng số của ảnh cho một người liên hệ thô. Nối giá trị này vào hằng số DisplayPhoto.CONTENT_URI để lấy URI nội dung trỏ đến một tệp ảnh, sau đó gọi openAssetFileDescriptor() để lấy tên xử lý cho tệp ảnh.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
Một URI nội dung trỏ trực tiếp đến tệp ảnh của ảnh được biểu thị bằng hàng này. Gọi openAssetFileDescriptor() bằng URI này để xử lý tệp ảnh.

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

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

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

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

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

Dữ liệu luồng mạng xã hội do 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ị, cung cấp một cách hiệu quả để kết nối hệ thống mạng xã hội với các danh bạ hiện có. Bạn có thể sử dụng các tính năng sau:

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

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

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

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

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

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

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

Ý định đến chứa URI nội dung của mặ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 mục văn bản và ảnh, hãy sử dụng cả hai thuộc tính trong cùng một tệp.

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

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

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

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

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

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

<Danh bạAccountType> phần tử

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

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

có trong:

res/xml/contacts.xml

có thể chứa:

<ContactsDataKind>

Description:

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

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

Thuộc tính:

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

Thuộc tính này cho phép giá trị nhận dạng tài nguyên chuỗi.

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

Phần tử <ContactsDataKind>

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

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

có trong:

<ContactsAccountType>

Description:

Sử dụng phần tử này để ứng dụng danh bạ hiển thị nội dung của hàng dữ liệu tuỳ chỉnh dưới dạng một phần chi tiết của một liên hệ thô. Mỗi phần tử con <ContactsDataKind> của <ContactsAccountType> đại diện cho một loại hàng dữ liệu tuỳ chỉnh mà trình chuyển đổi đồng bộ hoá của bạn thêm vào bảng ContactsContract.Data. Thêm một phần tử <ContactsDataKind> cho mỗi loại MIME tuỳ chỉnh mà bạn sử dụng. Bạn không có để thêm phần tử nếu bạn 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 của mình trong Bảng ContactsContract.Data. Ví dụ: giá trị vnd.android.cursor.item/vnd.example.locationstatus có thể là một loại MIME tuỳ chỉnh cho một hàng dữ liệu ghi lại vị trí đã biết gần đây nhất của một người liên hệ.
android:icon
Tài nguyên có thể vẽ của Android mà ứng dụng danh bạ hiển thị bên cạnh dữ liệu của bạn. Sử dụng đoạn mã này để biểu thị cho người dùng rằng dữ liệu được thu thập từ dịch vụ của bạn.
android:summaryColumn
Tên cột của giá trị đầu tiên trong số hai giá trị được truy xuất từ hàng dữ liệu. Giá trị này sẽ hiển thị dưới dạng dòng đầu tiên của mục nhập cho hàng dữ liệu này. Dòng đầu tiên được dùng để tóm tắt dữ liệu, nhưng không bắt buộc. Xem thêm android:detailColumn.
android:detailColumn
Tên cột cho giá trị thứ hai trong hai giá trị được truy xuất từ hàng dữ liệu. Giá trị là được hiển thị dưới dạng dòng thứ hai của mục nhập cho hàng dữ liệu này. Xem thêm android:summaryColumn.

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

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

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

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

Trình cung cấp danh bạ có thể tùy chọn gắn nhãn bộ sưu tập các địa chỉ liên hệ có liên quan bằng nhóm. Nếu máy chủ được liên kết với tài khoản người dùng Nếu muốn duy trì các nhóm, bộ điều hợp đồng bộ hoá cho loại tài khoản của tài khoản sẽ chuyển nhóm dữ liệu giữa Trình cung cấp danh bạ và máy chủ. Khi người dùng thêm một người liên hệ mới vào máy chủ rồi đưa địa chỉ 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 mà một người liên hệ thô thuộc về được lưu trữ trong bảng ContactsContract.Data, sử dụng loại MIME ContactsContract.CommonDataKinds.GroupMembership.

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

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

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