Получить информацию о контакте

В этом уроке показано, как получить подробные данные о контакте, такие как адреса электронной почты, номера телефонов и т. д. Это детали, которые пользователи ищут при получении контакта. Вы можете предоставить им все сведения о контакте или отобразить только сведения определенного типа, например адреса электронной почты.

В шагах этого урока предполагается, что у вас уже есть строка ContactsContract.Contacts для контакта, выбранного пользователем. На уроке «Получение имен контактов» показано, как получить список контактов.

Получить всю информацию о контакте

Чтобы получить все сведения о контакте, найдите в таблице ContactsContract.Data все строки, содержащие LOOKUP_KEY контакта. Этот столбец доступен в таблице ContactsContract.Data , поскольку поставщик контактов выполняет неявное соединение между таблицей ContactsContract.Contacts и таблицей ContactsContract.Data . Более подробно столбец LOOKUP_KEY описан в уроке «Получение имен контактов» .

Примечание. Получение всех сведений о контакте снижает производительность устройства, поскольку ему необходимо получить все столбцы в таблице ContactsContract.Data . Прежде чем использовать этот метод, оцените влияние на производительность.

Запросить разрешения

Для чтения данных от поставщика контактов ваше приложение должно иметь разрешение READ_CONTACTS . Чтобы запросить это разрешение, добавьте следующий дочерний элемент <manifest> в файл манифеста:

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

Настройка проекции

В зависимости от типа данных, содержащихся в строке, она может использовать только несколько столбцов или много. Кроме того, данные находятся в разных столбцах в зависимости от типа данных. Чтобы гарантировать получение всех возможных столбцов для всех возможных типов данных, вам необходимо добавить в проекцию все имена столбцов. Всегда извлекайте Data._ID , если вы привязываете результат Cursor к ListView ; в противном случае привязка не будет работать. Также извлеките Data.MIMETYPE , чтобы можно было определить тип данных каждой получаемой строки. Например:

Котлин

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
)

Ява

    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
            };

Эта проекция извлекает все столбцы строки в таблице ContactsContract.Data , используя имена столбцов, определенные в классе ContactsContract.Data .

При желании вы также можете использовать любые другие константы столбцов, определенные в классе ContactsContract.Data или унаследованные им. Однако обратите внимание, что столбцы SYNC1 до SYNC4 предназначены для использования адаптерами синхронизации, поэтому их данные бесполезны.

Определите критерии выбора

Определите константу для вашего предложения выбора, массив для хранения аргументов выбора и переменную для хранения значения выбора. Используйте столбец Contacts.LOOKUP_KEY чтобы найти контакт. Например:

Котлин

// 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

Ява

    // 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

С использованием "?" в качестве заполнителя в текстовом выражении выбора гарантирует, что результирующий поиск будет создан путем привязки, а не компиляции SQL. Такой подход исключает возможность вредоносного внедрения SQL.

Определить порядок сортировки

Определите желаемый порядок сортировки в результирующем Cursor . Чтобы сохранить все строки для определенного типа данных вместе, выполните сортировку по Data.MIMETYPE . Этот аргумент запроса группирует все строки электронной почты, все строки телефонов и т. д. Например:

Котлин

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

Ява

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

Примечание. Некоторые типы данных не используют подтип, поэтому сортировку по подтипу невозможно выполнить. Вместо этого вам придется перебрать возвращенный Cursor , определить тип данных текущей строки и сохранить данные для строк, которые используют подтип. Когда вы закончите чтение курсора, вы сможете отсортировать каждый тип данных по подтипу и отобразить результаты.

Инициализируйте загрузчик

Всегда выполняйте получение данных от поставщика контактов (и всех других поставщиков контента) в фоновом потоке. Используйте платформу Loader, определенную классом LoaderManager и интерфейсом LoaderManager.LoaderCallbacks для фонового извлечения данных.

Когда вы будете готовы получить строки, инициализируйте платформу загрузчика, вызвав initLoader() . Передайте методу целочисленный идентификатор; этот идентификатор передается методам LoaderManager.LoaderCallbacks . Идентификатор помогает использовать несколько загрузчиков в приложении, позволяя различать их.

В следующем фрагменте показано, как инициализировать платформу загрузчика:

Котлин

// 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)

Ява

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);

Реализовать onCreateLoader()

Реализуйте метод onCreateLoader() , который вызывается платформой загрузчика сразу после вызова initLoader() . Верните CursorLoader из этого метода. Поскольку вы ищете в таблице ContactsContract.Data , используйте константу Data.CONTENT_URI в качестве URI контента. Например:

Котлин

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
}

Ява

@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
                    );
    }

Реализуйте onLoadFinished() и onLoaderReset().

Реализуйте метод onLoadFinished() . Платформа загрузчика вызывает onLoadFinished() , когда поставщик контактов возвращает результаты запроса. Например:

Котлин

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

Ява

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

Метод onLoaderReset() вызывается, когда платформа загрузчика обнаруживает, что данные, подтверждающие результат Cursor изменились. На этом этапе удалите все существующие ссылки на Cursor , установив для них значение null. Если вы этого не сделаете, платформа загрузчика не уничтожит старый Cursor , и вы получите утечку памяти. Например:

Котлин

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

Ява

    @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;
    }

Получение конкретных сведений о контакте

Получение определенного типа данных для контакта, например всех электронных писем, происходит по той же схеме, что и получение всех сведений. Это единственные изменения, которые вам нужно внести в код, указанный в разделе Получение всех сведений о контакте :

Проекция
Измените свою проекцию, чтобы получить столбцы, относящиеся к типу данных. Также измените проекцию, чтобы использовать константы имен столбцов, определенные в подклассе ContactsContract.CommonDataKinds соответствующие типу данных.
Выбор
Измените текст выделения, чтобы найти значение MIMETYPE , соответствующее вашему типу данных.
Порядок сортировки
Поскольку вы выбираете только один тип детализации, не группируйте возвращаемый Cursor по Data.MIMETYPE .

Эти модификации описаны в следующих разделах.

Определить проекцию

Определите столбцы, которые вы хотите получить, используя константы имен столбцов в подклассе ContactsContract.CommonDataKinds для типа данных. Если вы планируете привязать Cursor к ListView , обязательно получите столбец _ID . Например, чтобы получить данные электронной почты, определите следующую проекцию:

Котлин

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

Ява

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

Обратите внимание, что в этой проекции используются имена столбцов, определенные в классе ContactsContract.CommonDataKinds.Email , вместо имен столбцов, определенных в классе ContactsContract.Data . Использование имен столбцов, специфичных для электронной почты, делает код более читабельным.

В проекции вы также можете использовать любые другие столбцы, определенные в подклассе ContactsContract.CommonDataKinds .

Определить критерии выбора

Определите текстовое выражение поиска, которое извлекает строки для LOOKUP_KEY определенного контакта и Data.MIMETYPE нужных вам сведений. Заключите значение MIMETYPE в одинарные кавычки, объединив символ " ' " (одинарные кавычки) в начало и конец константы; в противном случае поставщик интерпретирует константу как имя переменной, а не как строковое значение. Вам не нужно использовать заполнитель для этого значения, поскольку вы используете константу, а не значение, введенное пользователем. Например:

Котлин

/*
 * 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("")

Ява

    /*
     * 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 = { "" };

Определить порядок сортировки

Определите порядок сортировки возвращаемого Cursor . Поскольку вы получаете определенный тип данных, опустите сортировку MIMETYPE . Вместо этого, если тип искомых подробных данных включает подтип, выполните сортировку по нему. Например, данные электронной почты можно отсортировать по Email.TYPE :

Котлин

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

Ява

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