Truy xuất thông tin chi tiết về người liên hệ

Bài học này sẽ hướng dẫn cách truy xuất dữ liệu chi tiết về một người liên hệ, chẳng hạn như địa chỉ email, số điện thoại, v.v. Đó là thông tin mà người dùng đang tìm kiếm khi truy xuất một mục liên hệ. Bạn có thể cung cấp cho họ tất cả thông tin về một người liên hệ hoặc chỉ cho họ xem thông tin thuộc một loại cụ thể, chẳng hạn như địa chỉ email.

Các bước trong bài học này giả định rằng bạn đã có một hàng ContactsContract.Contacts cho một người liên hệ mà người dùng đã chọn. Bài học Truy xuất tên người liên hệ cho biết cách truy xuất danh sách người liên hệ.

Truy xuất tất cả thông tin chi tiết về một người liên hệ

Để truy xuất toàn bộ thông tin chi tiết của một người liên hệ, hãy tìm kiếm trong bảng ContactsContract.Data để tìm những hàng có chứa LOOKUP_KEY của người liên hệ đó. Cột này có trong bảng ContactsContract.Data vì Trình cung cấp danh bạ tạo mối liên kết ngầm giữa bảng ContactsContract.Contacts và bảng ContactsContract.Data. Cột LOOKUP_KEY được mô tả chi tiết hơn trong bài học Truy xuất tên người liên hệ.

Lưu ý: Việc truy xuất tất cả thông tin chi tiết của một người liên hệ sẽ làm giảm hiệu suất của thiết bị vì thiết bị cần truy xuất tất cả cột trong bảng ContactsContract.Data. Hãy xem xét tác động đến hiệu suất trước khi bạn sử dụng kỹ thuật này.

Yêu cầu cấp quyền

Để đọc dữ liệu từ Trình cung cấp danh bạ, ứng dụng của bạn phải có quyền READ_CONTACTS. Để yêu cầu quyền này, hãy thêm phần tử con sau đây của <manifest> vào tệp kê khai:

    <uses-permission android:name="android.permission.READ_CONTACTS" />

Thiết lập một phép chiếu

Tuỳ thuộc vào loại dữ liệu mà một hàng chứa, hàng đó có thể chỉ sử dụng một vài hoặc nhiều cột. Ngoài ra, dữ liệu nằm trong các cột khác nhau tuỳ thuộc vào loại dữ liệu. Để đảm bảo có được tất cả các cột có thể có cho mọi kiểu dữ liệu hiện có, bạn cần thêm tất cả tên cột vào phép chiếu. Hãy luôn truy xuất Data._ID nếu bạn đang liên kết kết quả Cursor với một ListView; nếu không, liên kết này sẽ không hoạt động. Đồng thời, hãy truy xuất Data.MIMETYPE để bạn có thể xác định loại dữ liệu của mỗi hàng mà bạn truy xuất. Ví dụ:

Kotlin

private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Data._ID,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.DATA2,
        ContactsContract.Data.DATA3,
        ContactsContract.Data.DATA4,
        ContactsContract.Data.DATA5,
        ContactsContract.Data.DATA6,
        ContactsContract.Data.DATA7,
        ContactsContract.Data.DATA8,
        ContactsContract.Data.DATA9,
        ContactsContract.Data.DATA10,
        ContactsContract.Data.DATA11,
        ContactsContract.Data.DATA12,
        ContactsContract.Data.DATA13,
        ContactsContract.Data.DATA14,
        ContactsContract.Data.DATA15
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.Data._ID,
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.DATA1,
                ContactsContract.Data.DATA2,
                ContactsContract.Data.DATA3,
                ContactsContract.Data.DATA4,
                ContactsContract.Data.DATA5,
                ContactsContract.Data.DATA6,
                ContactsContract.Data.DATA7,
                ContactsContract.Data.DATA8,
                ContactsContract.Data.DATA9,
                ContactsContract.Data.DATA10,
                ContactsContract.Data.DATA11,
                ContactsContract.Data.DATA12,
                ContactsContract.Data.DATA13,
                ContactsContract.Data.DATA14,
                ContactsContract.Data.DATA15
            };

Phép chiếu này truy xuất tất cả cột cho một hàng trong bảng ContactsContract.Data bằng cách sử dụng tên cột được xác định trong lớp ContactsContract.Data.

Bạn cũng có thể tuỳ ý sử dụng bất kỳ hằng số cột nào khác được xác định trong hoặc kế thừa bằng lớp ContactsContract.Data. Tuy nhiên, hãy lưu ý rằng các cột từ SYNC1 đến SYNC4 sẽ dành cho các bộ chuyển đổi đồng bộ hoá, vì vậy, dữ liệu của các cột này sẽ không hữu ích.

Xác định tiêu chí lựa chọn

Hãy xác định hằng số cho mệnh đề lựa chọn, một mảng để lưu các đối số lựa chọn và một biến để lưu giá trị lựa chọn. Sử dụng cột Contacts.LOOKUP_KEY để tìm người liên hệ đó. Ví dụ:

Kotlin

// Defines the selection clause
private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")
/*
 * Defines a variable to contain the selection value. Once you
 * have the Cursor from the Contacts table, and you've selected
 * the desired row, move the row's LOOKUP_KEY value into this
 * variable.
 */
private var lookupKey: String? = null

Java

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };
    /*
     * Defines a variable to contain the selection value. Once you
     * have the Cursor from the Contacts table, and you've selected
     * the desired row, move the row's LOOKUP_KEY value into this
     * variable.
     */
    private lateinit var lookupKey: String

Việc sử dụng dấu "?" làm phần giữ chỗ trong biểu thức văn bản lựa chọn đảm bảo rằng nội dung tìm kiếm kết quả được tạo bằng cách liên kết thay vì biên dịch SQL. Phương pháp này giúp loại bỏ khả năng chèn SQL độc hại.

Xác định thứ tự sắp xếp

Xác định thứ tự sắp xếp bạn muốn trong Cursor thu được. Để giữ tất cả các hàng cho một loại dữ liệu cụ thể với nhau, hãy sắp xếp theo Data.MIMETYPE. Đối số truy vấn này nhóm tất cả các hàng email lại với nhau, tất cả các hàng trong điện thoại với nhau, v.v. Ví dụ:

Kotlin

/*
 * Defines a string that specifies a sort order of MIME type
 */
private const val SORT_ORDER = ContactsContract.Data.MIMETYPE

Java

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;

Lưu ý: Một số loại dữ liệu không sử dụng loại phụ, vì vậy, bạn không thể sắp xếp theo loại phụ. Thay vào đó, bạn phải lặp lại thông qua Cursor được trả về, xác định loại dữ liệu của hàng hiện tại và lưu trữ dữ liệu cho các hàng sử dụng loại phụ. Khi đọc xong con trỏ, bạn có thể sắp xếp từng loại dữ liệu theo loại phụ và hiển thị kết quả.

Khởi chạy Trình tải

Luôn truy xuất từ Trình cung cấp danh bạ (và mọi trình cung cấp nội dung khác) trong một luồng ở chế độ nền. Sử dụng khung Trình tải do lớp LoaderManager và giao diện LoaderManager.LoaderCallbacks xác định để thực hiện các hoạt động truy xuất ở chế độ nền.

Khi bạn đã sẵn sàng truy xuất các hàng, hãy khởi chạy khung trình tải bằng cách gọi initLoader(). Truyền một giá trị nhận dạng bằng số nguyên vào phương thức; giá trị nhận dạng này được chuyển đến các phương thức LoaderManager.LoaderCallbacks. Giá trị nhận dạng này giúp bạn sử dụng nhiều trình tải trong một ứng dụng bằng cách cho phép bạn phân biệt giữa những trình tải đó.

Đoạn mã sau đây cho biết cách khởi chạy khung trình tải:

Kotlin

// Defines a constant that identifies the loader
private const val DETAILS_QUERY_ID: Int = 0

class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Initializes the loader framework
        loaderManager.initLoader(DETAILS_QUERY_ID, null, this)

Java

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    static int DETAILS_QUERY_ID = 0;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // Initializes the loader framework
        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);

Triển khai onCreateLoader()

Triển khai phương thức onCreateLoader(). Phương thức này được khung trình tải gọi ngay sau khi bạn gọi initLoader(). Trả về một CursorLoader từ phương thức này. Vì bạn đang tìm kiếm bảng ContactsContract.Data, hãy sử dụng hằng số Data.CONTENT_URI làm URI nội dung. Ví dụ:

Kotlin

override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
    // Choose the proper action
    mLoader = when(loaderId) {
        DETAILS_QUERY_ID -> {
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey
            // Starts the query
            activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        SORT_ORDER
                )
            }
        }
        ...
    }
    return mLoader
}

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            ContactsContract.Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            selectionArgs,
                            SORT_ORDER
                    );
    }

Triển khai onLoadFinish() và onLoaderReset()

Triển khai phương thức onLoadFinished(). Khung trình tải sẽ gọi onLoadFinished() khi Trình cung cấp danh bạ trả về kết quả của truy vấn. Ví dụ:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        when(loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * Process the resulting Cursor here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                break;
            ...
        }
    }

Phương thức onLoaderReset() được gọi khi khung trình tải phát hiện thấy dữ liệu hỗ trợ kết quả Cursor đã thay đổi. Tại thời điểm này, hãy xoá mọi tham chiếu hiện có đối với Cursor bằng cách đặt các giá trị này thành giá trị rỗng. Nếu không, khung trình tải sẽ không huỷ bỏ Cursor cũ và bạn sẽ bị rò rỉ bộ nhớ. Ví dụ:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        when (loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
                }
                break;
    }

Truy xuất thông tin cụ thể của người liên hệ

Việc truy xuất một loại dữ liệu cụ thể cho một người liên hệ, chẳng hạn như tất cả email, tuân theo cùng một mẫu như khi truy xuất tất cả thông tin chi tiết. Đây là những thay đổi duy nhất bạn cần thực hiện đối với mã nêu trong phần Truy xuất tất cả thông tin về người liên hệ:

Dự đoán
Sửa đổi phép chiếu để truy xuất các cột dành riêng cho kiểu dữ liệu. Ngoài ra, hãy sửa đổi phép chiếu để sử dụng các hằng số tên cột đã xác định trong lớp con ContactsContract.CommonDataKinds tương ứng với loại dữ liệu.
Lựa chọn
Sửa đổi văn bản lựa chọn để tìm kiếm giá trị MIMETYPE dành riêng cho loại dữ liệu của bạn.
Thứ tự sắp xếp
Vì bạn chỉ chọn một loại chi tiết duy nhất, nên đừng nhóm Cursor được trả về theo Data.MIMETYPE.

Những nội dung sửa đổi này được mô tả trong các phần sau.

Xác định phép chiếu

Xác định các cột bạn muốn truy xuất, sử dụng hằng số tên cột trong lớp con của ContactsContract.CommonDataKinds cho loại dữ liệu. Nếu bạn định liên kết Cursor với một ListView, hãy nhớ truy xuất cột _ID. Ví dụ: để truy xuất dữ liệu email, hãy xác định phép chiếu sau:

Kotlin

private val PROJECTION: Array<String> = arrayOf(
        ContactsContract.CommonDataKinds.Email._ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.CommonDataKinds.Email.TYPE,
        ContactsContract.CommonDataKinds.Email.LABEL
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL
            };

Xin lưu ý rằng phép chiếu này sử dụng tên cột được xác định trong lớp ContactsContract.CommonDataKinds.Email, thay vì tên cột được xác định trong lớp ContactsContract.Data. Việc sử dụng tên cột dành riêng cho email sẽ giúp mã dễ đọc hơn.

Trong phép chiếu, bạn cũng có thể sử dụng cột bất kỳ nào khác được xác định trong lớp con ContactsContract.CommonDataKinds.

Xác định tiêu chí lựa chọn

Xác định biểu thức văn bản tìm kiếm để truy xuất các hàng cho LOOKUP_KEY của một địa chỉ liên hệ cụ thể và Data.MIMETYPE của thông tin chi tiết mà bạn muốn. Chứa giá trị MIMETYPE trong dấu ngoặc đơn bằng cách nối ký tự "'" (dấu ngoặc đơn) với đầu và cuối của hằng số; nếu không, trình cung cấp sẽ diễn giải hằng số là tên biến thay vì giá trị chuỗi. Bạn không cần sử dụng phần giữ chỗ cho giá trị này vì bạn đang dùng hằng số thay vì giá trị do người dùng cung cấp. Ví dụ:

Kotlin

/*
 * Defines the selection clause. Search for a lookup key
 * and the Email MIME type
 */
private const val SELECTION =
        "${ContactsContract.Data.LOOKUP_KEY} = ? AND " +
        "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")

Java

    /*
     * Defines the selection clause. Search for a lookup key
     * and the Email MIME type
     */
    private static final String SELECTION =
            Data.LOOKUP_KEY + " = ?" +
            " AND " +
            Data.MIMETYPE + " = " +
            "'" + Email.CONTENT_ITEM_TYPE + "'";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };

Xác định thứ tự sắp xếp

Xác định thứ tự sắp xếp cho Cursor được trả về. Vì bạn đang truy xuất một loại dữ liệu cụ thể, hãy bỏ qua cách sắp xếp trên MIMETYPE. Thay vào đó, nếu loại dữ liệu chi tiết bạn đang tìm kiếm bao gồm một loại phụ, hãy sắp xếp dựa trên loại dữ liệu đó. Ví dụ: đối với dữ liệu email, bạn có thể sắp xếp trên Email.TYPE:

Kotlin

private const val SORT_ORDER: String = "${Email.TYPE} ASC"

Java

    private static final String SORT_ORDER = Email.TYPE + " ASC ";