page.title=Поставщик контактов @jd:body
Поставщик контактов представляет собой эффективный и гибкий компонент Android, который управляет центральным репозиторием устройства, в котором хранятся пользовательские данные. Поставщик контактов — это источник данных, которые отображаются в приложении «Контакты» на вашем устройстве. Вы также можете получить доступ к этим данным в своем собственном приложении и организовать обмен такими данными между устройством и службами в Интернете. Поставщик взаимодействует с широким набором источников данных и пытается организовать управление как можно большим набором данных о каждом человеке, поэтому организация поставщика довольно сложная. По этой причине API поставщика содержит широкий набор классов-контрактов и интерфейсов, отвечающих как за получение данных, так и за их изменение.
В этом руководстве рассматриваются следующие вопросы:
При изучении данного материала подразумевается, что вы уже знакомы с основами поставщиков контента Android. Дополнительные сведения о поставщиках контента Android представлены в руководстве Основные сведения о поставщике контента. Пример адаптера синхронизации служит примером использования такого приложения для обмена данными между поставщиком контактов и приложением, размещенным в веб-службах Google.
Поставщик контактов представляет собой поставщик контента Android. Он содержит в себе три типа данных о пользователе, каждый из которых указан в отдельной таблице, предоставляемой поставщиком, как показано на рисунке 1.
В качестве имен этих трех таблиц обычно используются названия соответствующих классов-контрактов. Эти классы определяют константы для URI контента, названий столбцов и значений в столбцах этих таблиц.
Другие таблицы, представленные классами-контрактами в {@link android.provider.ContactsContract}, представляют собой вспомогательные таблицы, которые поставщик контактов использует для управления своими операциями или поддержки определенных функций, имеющихся в приложении устройства «Контакты» или приложениях для телефонной связи.
Необработанный контакт представляет собой данные о пользователе, поступающие из одного аккаунта определенного типа. Поскольку в качестве источника данных о пользователе в поставщике контактов может выступать сразу несколько онлайн-служб, для одного и того же пользователя в поставщике контактов может существовать несколько необработанных контактов. Это также позволяет пользователю объединять пользовательские данные из нескольких аккаунтов одного и того же типа.
Большая часть данных необработанного контакта не хранится в таблице {@link android.provider.ContactsContract.RawContacts}. Вместо этого они хранятся в одной или нескольких строках в таблице {@link android.provider.ContactsContract.Data}. В каждой строке данных имеется столбец {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID}, в котором содержится значение {@code android.provider.BaseColumns#_ID RawContacts._ID} его родительской строки{@link android.provider.ContactsContract.RawContacts}.
В таблице 1 указаны столбцы таблицы {@link android.provider.ContactsContract.RawContacts}, которые имеют большое значение. Обязательно ознакомьтесь с примечаниями, приведенными после этой таблицы.
Название столбца | Использование | Примечания |
---|---|---|
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} | Имя аккаунта для типа аккаунта, который выступает в роли источника данных для необработанного контакта. Например, имя аккаунта Google является одним из эл. адресов почты Gmail владельца устройства. Дополнительные сведения см. в следующей записи {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}. | Формат этого имени зависит от типа аккаунта. Это не обязательно адрес эл. почты. |
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} |
Тип аккаунта, который выступает в роли источника данных для необработанного контакта. Например, тип аккаунта
Google — com.google . Всегда указывайте тип аккаунта
и идентификатор домена, которым вы владеете или управляете. Это позволит гарантировать уникальность
типа вашего аккаунта.
|
У типа аккаунта, выступающего в роли источника данных контактов, обычно имеется связанный с ним адаптер синхронизации, который синхронизирует данные с поставщиком контактов. |
{@link android.provider.ContactsContract.RawContactsColumns#DELETED} | Флаг deleted для необработанного контакта. | Этот флаг позволяет поставщику контактов хранить строку внутри себя до тех пор, пока адаптеры синхронизации не смогут удалить эту строку с серверов, а затем удалить ее из репозитория. |
Ниже представлены важные примечания к таблице {@link android.provider.ContactsContract.RawContacts}.
Например, если необходимо, чтобы в вашем приложении хранились данные контактов для веб-службы в домене {@code com.example.dataservice}, и аккаунт пользователя службы выглядит следующим образом:{@code becky.sharp@dataservice.example.com}, то пользователю сначала необходимо добавить «тип» аккаунта ({@code com.example.dataservice}) и его «имя» ({@code becky.smart@dataservice.example.com}) прежде чем ваше приложение сможет добавлять строки необработанных контактов. Это требование можно указать в документации для пользователей, а также можно предложить пользователю добавить тип и имя своего аккаунта, либо реализовать оба этих варианта. Более подробно типы и имена аккаунтов рассматриваются в следующем разделе.
Чтобы понять, что такое необработанный контакт, рассмотрим пример с пользователем Emily Dickinson, на устройстве которой имеется три следующих аккаунта:
emily.dickinson@gmail.com
;emilyd@gmail.com
;Она включила функцию Синхронизировать контакты для всех трех этих аккаунтов в настройках Аккаунты.
Предположим, что Emily Dickinson открывает браузер, входит в Gmail под именем
emily.dickinson@gmail.com
, затем открывает
Контакты и добавляет новый контакт Thomas Higginson. Позже она снова входит в Gmail под именем
emilyd@gmail.com
и отправляет письмо пользователю Thomas Higginson, который автоматически
добавляется в ее контакты. Также она подписана на новости от colonel_tom (аккаунт пользователя Thomas Higginson в Twitter) в
Twitter.
В результате этих действий поставщик контактов создает три необработанных контакта:
emily.dickinson@gmail.com
.
Тип этого аккаунта — Google.
emilyd@gmail.com
.
Тип этого аккаунта — также Google. Второй необработанный контакт создается даже в том случае,
если его имя полностью совпадает с именем предыдущего контакта, поскольку первый
был добавлен для другого аккаунта.
Как уже указывалось ранее, данные необработанного контакта
хранятся в строке {@link android.provider.ContactsContract.Data}, которая связана со значением
_ID
необработанного контакта. Благодаря этому для одного необработанного контакта может существовать несколько экземпляров
одного и того же типа данных (например, адресов эл. почты или номеров телефонов). Например, если у контакта
Thomas Higginson для аккаунта {@code emilyd@gmail.com} (строка необработанного контакта Thomas Higginson,
связанная с учетной записью Google emilyd@gmail.com
) имеется адрес эл. почты
thigg@gmail.com
и служебный адрес эл. почты
thomas.higginson@gmail.com
, то поставщик контактов сохраняет две строки
адреса эл. почты и связывает их с этим необработанным контактом.
Обратите внимание, что в этой таблице хранятся данные разных типов. Строки со сведениями об отображаемом имени, номере телефона, адресе эл. почты, почтовом адресе, фото и веб-сайте хранятся в таблице {@link android.provider.ContactsContract.Data}. Для упрощения управления этими данными в таблице {@link android.provider.ContactsContract.Data} предусмотрены столбцы с описательными именами, а также другие столбцы с универсальными именами. Содержимое в столбце с описательным именем имеет то же значение, независимо от типа данных в строке, тогда как содержимое столбца с универсальным именем может иметь разное значение в зависимости от типа данных.
Вот некоторые примеры столбцов с описательными именами:
_ID
необработанного контакта для этих данных.
Существует 15 общедоступных столбцов с универсальными именами (DATA1
–DATA15
)
и четыре дополнительных столбца
(SYNC1
–SYNC4
), которые используются только адаптерами
синхронизации. Константы столбцов с универсальными именами применяются всегда, независимо от типа данных,
содержащихся в столбце.
Столбец DATA1
является индексируемым. Поставщик контактов всегда использует этот столбец для
данных, которые, как он ожидает, будут наиболее часто являться целевыми в запросах. Например,
в строке с адресами эл. почты в этом столбце указывается фактический адрес эл. почты.
Обычно столбец DATA15
зарезервирован для данных больших двоичных объектов
(BLOB), таких как миниатюры фотографий.
Для упрощения работы со столбцами определенного типа строк в поставщике контактов также предусмотрены константы для имен столбцов по типам строк, которые определены в подклассах класса {@link android.provider.ContactsContract.CommonDataKinds}. Эти константы просто присваивают одному и тому же имени столбца различные константы, что позволяет вам получать доступ к данным в строке определенного типа.
Например, класс {@link android.provider.ContactsContract.CommonDataKinds.Email} определяет константы имени столбца для строки {@link android.provider.ContactsContract.Data}, в которой имеется тип MIME {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE Email.CONTENT_ITEM_TYPE}. В этом классе содержится константа {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} для столбца адреса эл. почты. Фактическое значение {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} — data1, которое совпадает с универсальным именем столбца.
Внимание! Не добавляйте свои настраиваемые данные в таблицу
{@link android.provider.ContactsContract.Data} с помощью строки, в которой имеется один из
предварительно заданных поставщиком типов MIME. В противном случае вы можете потерять данные или вызвать неполадки
в работе поставщика. Например, не следует добавлять строку с типом MIME
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
Email.CONTENT_ITEM_TYPE}, в которой в столбце
DATA1
вместо адреса эл. почты содержится имя пользователя. Если вы укажете в строке собственный настраиваемый тип MIME, вы можете свободно
указывать собственные имена столбцов по типам строк и использовать их так, как пожелаете.
На рисунке 2 показано, как столбцы с описательными именами и столбцы данных отображаются в строке {@link android.provider.ContactsContract.Data}, а также как имена столбцов по типу строк «накладываются» на универсальные имена столбцов.
В таблице 2 перечислены наиболее часто используемые классы имен столбцов по типам строк.
Класс сопоставления | Тип данных | Примечания |
---|---|---|
{@link android.provider.ContactsContract.CommonDataKinds.StructuredName} | Данные об имени необработанного контакта, связанного с этой строкой данных. | У необработанного контакта имеется только одна строка такого типа. |
{@link android.provider.ContactsContract.CommonDataKinds.Photo} | Основная фотография необработанного контакта, связанного с этой строкой данных. | У необработанного контакта имеется только одна строка такого типа. |
{@link android.provider.ContactsContract.CommonDataKinds.Email} | Адрес эл. почты необработанного контакта, связанного с этой строкой данных. | У необработанного контакта может быть несколько адресов эл. почты. |
{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal} | Почтовый адрес необработанного контакта, связанного с этой строкой данных. | У необработанного контакта может быть несколько почтовых адресов. |
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} | Идентификатор, с помощью которого необработанный контакт связан с одной из групп в поставщике контактов. | Группы представляют собой необязательный компонент для типа аккаунта и имени аккаунта. Дополнительные сведения о группах представлены в разделе Группы контактов. |
Поставщик контактов объединяет строки с необработанными контактами для всех типов аккаунтов и имен аккаунтов для создания контакта. Это позволяет упростить отображение и изменение всех данных, собранных в отношении пользователя. Поставщик контактов управляет процессом создания строк контактов и агрегированием необработанных контактов, у которых имеется строка контакта. Ни приложениям, ни адаптерам синхронизации не разрешается добавлять контакты, а некоторые столбцы в строке контакта доступны только для чтения.
Примечание. Если попытаться добавить контакт в поставщик контактов с помощью метода {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()}, будет выдано исключение {@link java.lang.UnsupportedOperationException}. Если вы попытаетесь обновить столбец, доступный только для чтения, это действие будет проигнорировано.
При добавлении нового необработанного контакта, который не соответствует ни одному из существующих контактов, поставщик контактов создает новый контакт. Поставщик поступает аналогично в случае, если данные в строке существующего необработанного контакта изменяются таким образом, что они больше не соответствуют контакту, с которым они ранее были связаны. При создании приложением или адаптером синхронизации нового контакта, который соответствует существующему контакту, то новый контакт объединяется с существующим контактом.
Поставщик контактов связывает строку контакта с его строками необработанного контакта посредством столбца
_ID
строки контакта в таблице
{@link android.provider.ContactsContract.Contacts Contacts}. Столбец CONTACT_ID
в таблице необработанных контактов
{@link android.provider.ContactsContract.RawContacts} содержит значения _ID
для
строки контактов, связанной с каждой строкой необработанных контактов.
В таблице {@link android.provider.ContactsContract.Contacts} также имеется столбец {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY}, который выступает в роли «постоянной ссылки» на строку контакта. Поскольку поставщик контактов автоматически сохраняет контакты, в ответ на агрегирование или синхронизацию он может изменить значение {@code android.provider.BaseColumns#_ID} строки контакта. Даже если это произойдет, URI контента {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, объединенный с {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} контакта, будет по-прежнему указывать на строку контакта, поэтому вы можете смело использовать {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} для сохранения ссылок на «избранные» контакты и др. Столбец имеет собственный формат, который не связан с форматом столбца{@code android.provider.BaseColumns#_ID}.
На рисунке 3 показаны взаимосвязи этих трех основных таблиц друг с другом.
Пользователь вводит данные контактов прямо на устройстве, однако данные также поступают в поставщик контактов из веб-служб посредством адаптеров синхронизации, что позволяет автоматизировать обмен данными между устройством и службами в Интернете. Адаптеры синхронизации выполняются в фоновом режиме под управлением системы, и для управления данными они вызывают методы {@link android.content.ContentResolver}.
В Android веб-служба, с которой работает адаптер синхронизации, определяется по типу аккаунта. Каждый адаптер синхронизации работает с одним типом аккаунта, однако он может поддерживать несколько имен аккаунтов такого типа. Вкратце типы и имена аккаунтов рассматриваются в разделе Источники данных необработанных контактов. Указанные ниже определения позволяют более точно охарактеризовать связь между типами и именами аккаунтов и адаптерами синхронизации и службами.
google.com
. Это значение соответствует типу аккаунта, используемого
{@link android.accounts.AccountManager}.
Типы аккаунтов не обязательно должны быть уникальными. Пользователь может создать несколько аккаунтов Google Контакты и загрузить данные из них в поставщик контактов; это может произойти в случае, если у пользователя имеется один набор персональных контактов для личного аккаунта, и другой набор — для служебного аккаунта. Имена аккаунтов обычно уникальные. Вместе они формируют определенный поток данных между поставщиком контактов и внешними службами.
Если необходимо передать данные из службы в поставщик контактов, необходимо создать собственный адаптер синхронизации. Дополнительные сведения об этом представлены в разделе Адаптеры синхронизации поставщика контактов.
На рисунке 4 показано, какую роль выполняет поставщик контактов в потоке передачи данных о пользователях. В области, отмеченной на рисунке как sync adapters, каждый адаптер промаркирован в соответствии с поддерживаемым им типом аккаунтов.
Приложения, которым требуется доступ к поставщику контактов, должны запросить указанные ниже разрешения.
AndroidManifest.xml
с элементом
<uses-permission>
в виде
<uses-permission android:name="android.permission.READ_CONTACTS">
.
AndroidManifest.xml
с элементом
<uses-permission>
в виде
<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Эти разрешения не распространяются на работу с данными профиля пользователя. Профиль пользователя и требуемые разрешения для работы с ним рассматриваются в разделе Профиль пользователя ниже.
Помните, что данные о контактах пользователя являются личными и конфиденциальными. Пользователи заботятся о своей конфиденциальности, поэтому не хотят, чтобы приложения собирали данные о них самих или их контактах. Если пользователю не до конца ясно, для чего требуется разрешение на доступ к данным контактов, они могут присвоить вашему приложению низкий рейтинг или вообще откажутся его устанавливать.
В таблице {@link android.provider.ContactsContract.Contacts} имеется всего одна строка, содержащая
данные профиля для устройства пользователя. Эти данные описывают user
устройства,
а не один из контактов пользователя. Строка контактов профиля связана со строкой
необработанных контактов в каждой из систем, в которой используется профиль.
В каждой строке необработанного контакта профиля может содержаться несколько строк данных. Константы для доступа к профилю пользователя
представлены в классе {@link android.provider.ContactsContract.Profile}.
Для доступа к профилю пользователя требуются особые разрешения. Кроме разрешений {@link android.Manifest.permission#READ_CONTACTS} и {@link android.Manifest.permission#WRITE_CONTACTS}, которые требуются для чтения и записи, для доступа к профилю пользователя необходимы разрешения {@code android.Manifest.permission#READ_PROFILE} и {@code android.Manifest.permission#WRITE_PROFILE} на чтение и запись соответственно.
Всегда следует помнить, что профиль пользователя представляет собой конфиденциальную информацию. Разрешение {@code android.Manifest.permission#READ_PROFILE} предоставляет вам доступ к личной информации на устройстве пользователя. В описании своего приложения обязательно укажите, для чего вам требуется доступ к профилю пользователя.
Чтобы получить строку контакта с профилем пользователя, вызовите метод {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) ContentResolver.query()}. Задайте для URI контента значение {@link android.provider.ContactsContract.Profile#CONTENT_URI} и не указывайте никаких критериев выборки. Этот URI контента также можно использовать в качестве основного URI для получения необработанных контактов или данных профиля. Ниже представлен фрагмент кода для получения данных профиля.
// Sets the columns to retrieve for the user profile mProjection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // Retrieves the profile from the Contacts Provider mProfileCursor = getContentResolver().query( Profile.CONTENT_URI, mProjection , null, null, null);
Примечание. Если при извлечении несколько строк контактов необходимо определить, какой из них является профилем пользователя, обратитесь к столбцу {@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} этой строки. Если в этом столбце указано значение «1», то в это строке находится профиль пользователя.
Поставщик контактов управляет данными, которые используются для отслеживания состояния данных контакта в репозитории. Эти метаданные о репозитории хранятся в различных местах, включая строки необработанных контактов, данные и строки таблицы контактов, таблицу {@link android.provider.ContactsContract.Settings} и таблицу {@link android.provider.ContactsContract.SyncState}. В таблице ниже указаны значения каждого из этих фрагментов метаданных.
Таблица | Столбец | Значения | Описание |
---|---|---|---|
{@link android.provider.ContactsContract.RawContacts} | {@link android.provider.ContactsContract.SyncColumns#DIRTY} | «0» — с момента последней синхронизации изменения не вносились. |
Служит для отметки необработанных контактов, которые были изменены на устройстве и которые необходимо снова синхронизировать с
сервером. Значение устанавливается автоматически поставщиком контактов при обновлении строк приложениями
Android.
Адаптеры синхронизации, которые вносят изменения в необработанные контакты или таблицы данных, должны всегда добавлять к используемому ими URI контента строку {@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER}. Это позволяет предотвратить пометку таких строк поставщиком как «грязных». В противном случае изменения, внесенные адаптером синхронизации, будут рассматриваться как локальные изменения, которые подлежат отправке на сервер, даже если источником таких изменений был сам сервер. |
«1» — с момента последней синхронизации были внесены изменения, которые необходимо отразить и на сервере. | |||
{@link android.provider.ContactsContract.RawContacts} | {@link android.provider.ContactsContract.SyncColumns#VERSION} | Номер версии этой строки. | Поставщик контактов автоматически увеличивает это значение при каждом изменении в строке или в связанных с ним данных. |
{@link android.provider.ContactsContract.Data} | {@link android.provider.ContactsContract.DataColumns#DATA_VERSION} | Номер версии этой строки. | Поставщик контактов автоматически увеличивает это значение при каждом изменении в строке данных. |
{@link android.provider.ContactsContract.RawContacts} | {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} | Строковое значение, которое служит уникальным идентификатором данного необработанного контакта в аккаунте, в котором он был создан. |
Когда адаптер синхронизации создает новый необработанный контакт, в этом столбце следует указать
уникальный идентификатор этого необработанного контакта на сервере. Когда же приложение Android создает
новый необработанный контакт, то приложению следует оставить этот столбец пустым. Это служит сигналом для
адаптера синхронизации, чтобы создать новый необработанный контакт на сервере и
получить значение {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}.
В частности, идентификатор источника должен быть уникальным для каждого типа аккаунта и неизменным при синхронизации.
|
{@link android.provider.ContactsContract.Groups} | {@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE} | «0» — контакты, представленные в этой группе, не должны отображаться в интерфейсах пользователя приложений Android. | Этот столбец предназначен для сведений о совместимости с серверами, позволяющими пользователям скрывать контакты в определенных группах. |
«1» — контакты, представленные в этой группе, могут отображаться в интерфейсах пользователя приложений. | |||
{@link android.provider.ContactsContract.Settings} | {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} | «0» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе, не отображаются в интерфейсах пользователя приложений Android. | По умолчанию контакты скрыты, если ни один из их необработанных контактов не принадлежит группе (принадлежность необработанного контакта к группе указывается в одном или нескольких строках {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} в таблице {@link android.provider.ContactsContract.Data}). Установив этот флаг в строке таблицы {@link android.provider.ContactsContract.Settings} для типа аккаунта и имени аккаунта, вы можете принудительно сделать видимыми контакты, не принадлежащие какой-либо группе. Один из вариантов использования этого флага — отображение контактов с серверов, на которых не используются группы. |
«1» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе, отображаются в интерфейсах пользователя приложений Android. | |||
{@link android.provider.ContactsContract.SyncState} | (все) | Эта таблица используется для хранения метаданных для вашего адаптера синхронизации. | С помощью этой таблицы вы можете на постоянной основе хранить на устройстве сведения о состоянии синхронизации и другие связанные с синхронизацией данные. |
В этом разделе рассматриваются инструкции по получению доступа к данным из поставщика контактов, в частности, следующие:
Дополнительные сведения о внесении изменений с помощью адаптеров синхронизации представлены в разделе Адаптеры синхронизации поставщика контактов.
Таблицы поставщика контактов имеют иерархическую структуру, поэтому зачастую полезно извлекать строку и все связанные с ней ее дочерние строки. Например, для отображения всей информации о пользователе вы, возможно, захотите извлечь все строки {@link android.provider.ContactsContract.RawContacts} для одной строки {@link android.provider.ContactsContract.Contacts} или все строки {@link android.provider.ContactsContract.CommonDataKinds.Email} для одной строки {@link android.provider.ContactsContract.RawContacts}. Чтобы упростить этот процесс, в поставщике контактов имеются объекты, которые выступают в роли соединителей базы данных между таблицами.
Объект представляет собой подобие таблицы, состоящей из выбранных столбцов родительской таблицы и ее дочерней таблицы. При запросе объекта вы предоставляете проекцию и критерии поиска на основе доступных в объекте столбцов. В результате вы получаете объект {@link android.database.Cursor}, в котором содержится одна строка для каждой извлеченной строки дочерней таблицы. Например, если запросить объект {@link android.provider.ContactsContract.Contacts.Entity} для имени контакта и все строки {@link android.provider.ContactsContract.CommonDataKinds.Email} для всех необработанных контактов, соответствующих этому имени, вы получите объект {@link android.database.Cursor}, содержащий по одной строке для каждой строки {@link android.provider.ContactsContract.CommonDataKinds.Email}.
Использование объектов упрощает запросы. С помощью объекта можно извлечь сразу все данные для контакта или необработанного контакта, вместо того, чтобы сначала делать запрос к родительской таблице для получения идентификатора и последующего запроса к дочерней таблице с использованием полученного идентификатора. Кроме того, поставщик контактов обрабатывает запрос объекта за одну транзакцию, что гарантирует внутреннюю согласованность полученных данных.
Примечание. Объект обычно содержит не все столбцы родительской и дочерней таблиц. Если вы попытаетесь изменить имя столбца, который отсутствует в списке констант имени столбца для объекта, вы получите {@link java.lang.Exception}.
Ниже представлен пример кода для получения всех строк необработанного контакта для контакта. Это фрагмент более крупного приложения, в котором имеются две операции: «основная» и «для получения сведений». Основная операция служит для получения списка строк контактов; при выборе пользователем контакта операция отправляет его идентификатор в операцию для получения сведений. Операция для получения сведений использует объект {@link android.provider.ContactsContract.Contacts.Entity} для отображения всех строк данных, полученных ото всех необработанных контактов, которые связаны с выбранным контактом.
Вот фрагмент кода операции «для получения сведений»:
... /* * 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). */ mContactUri = Uri.withAppendedPath( mContactUri, 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 mCursorAdapter = new SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor mFromColumns, // the columns in the cursor that provide the data mToViews, // the views in the view item that display the data 0); // flags // Sets the ListView's backing adapter. mRawContactList.setAdapter(mCursorAdapter); ... @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 mContactUri, // 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. }
По завершении загрузки {@link android.app.LoaderManager} выполняет обратный вызов метода {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) onLoadFinished()}. Одним их входящих аргументов для этого метода является {@link android.database.Cursor} с результатом запроса. В своем приложении вы можете получить данные из этого объекта {@link android.database.Cursor} для его отображения или дальнейшей работы с ним.
При каждой возможности данные в поставщике контактов следует вставлять, обновлять и удалять в «пакетном режиме» путем создания {@link java.util.ArrayList} из объектов {@link android.content.ContentProviderOperation} и вызова метода {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Поскольку поставщик контактов выполняет все операции в методе {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} за одну транзакцию, ваши изменения всегда будут находиться в рамках репозитория контактов и всегда будут согласованными. Пакетное изменение также упрощает вставку необработанного контакта одновременно с его подробными данными.
Примечание. Чтобы изменить отдельный необработанный контакт, рекомендуется отправить намерение в приложение для работы с контактами на устройстве вместо обработки изменения в вашем приложении. Более подробно эта операция описана в разделе Получение и изменение данных с помощью намерений.
Пакетное изменение большого количества операций может заблокировать выполнение других процессов,
что может негативно сказаться на работе пользователя с приложением. Чтобы упорядочить все необходимые изменения
в рамках как можно меньшего количества отдельных списков и при этом предотвратить
блокирование работы системы, следует задать пределы для одной или нескольких операций.
Предел представляет собой объект {@link android.content.ContentProviderOperation}, в качестве значения параметра
{@link android.content.ContentProviderOperation#isYieldAllowed()} которого задано
true
. При достижении поставщиком контактов предела он приостанавливает свою работу,
чтобы могли выполняться другие процессы, и закрывает текущую транзакцию. Когда поставщик запускается снова, он
выполняет следующую операцию в {@link java.util.ArrayList} и запускает
новую транзакцию.
Использование пределов позволяет за один вызов метода {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} выполнять более одной транзакции. По этой причине вам следует задать предел для последней операции с набором связанных строк. Например, необходимо задать предел для последней операции в наборе, которая добавляет строки необработанного контакта и связанные с ним строки данных, или для последней операции с набором строк, связанных с одним контактом.
Пределы также являются единицами атомарных операций. Все операции доступа в промежутке между двумя пределами завершаются либо успехом, либо сбоем в рамках одной единицы. Если любой из пределов не задан, наименьшей атомарной операцией считается весь пакет операций. Использование пределов позволяет предотвратить снижение производительности системы и одновременно обеспечить выполнение набора операций на атомарном уровне.
При вставке новой строки необработанного контакта и связанных с ним рядов данных в виде набора объектов {@link android.content.ContentProviderOperation} вам приходится связывать строки данных со строкой необработанного контакта путем вставки значения {@code android.provider.BaseColumns#_ID} необработанного контакта в виде значения {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Однако это значение недоступно, когда вы создаете{@link android.content.ContentProviderOperation} для строки данных, поскольку вы еще не применили {@link android.content.ContentProviderOperation} для строки необработанного контакта. Чтобы обойти это ограничение, в классе {@link android.content.ContentProviderOperation.Builder} предусмотрен метод {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}. С помощью этого метода можно вставлять или изменять столбец с результатом предыдущей операции.
В методе {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} предусмотрено два аргумента:
key
previousResult
previousResult
является индекс
одного из этих результатов, который извлекается и хранится со значением
key
. Благодаря этому можно вставить новую запись необработанного контакта и получить обратно его значение
{@code android.provider.BaseColumns#_ID}, а затем создать «обратную ссылку» на
значение при добавлении строки {@link android.provider.ContactsContract.Data}.
Целиком весь результат создается при первом вызове метода
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}.
Размер результата равен размеру {@link java.util.ArrayList} предоставленных вами объектов
{@link android.content.ContentProviderOperation}. Однако для всех
элементов в массиве результатов присваивается значение null
, и при попытке
воспользоваться обратной ссылкой на результат еще не выполненной операции метод
{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}
выдает {@link java.lang.Exception}.
Ниже представлены фрагменты кода для вставки нового необработанного контакта и его данных в пакетном режиме. Они включают
код, который задает предел и использует обратную ссылку. Эти фрагменты
представляют собой расширенную версию метода createContacEntry()
, который входит в класс
ContactAdder
в примере приложения
Contact Manager
.
Первый фрагмент кода служит для извлечения данных контакта из пользовательского интерфейса. На этом этапе пользователь уже выбрал аккаунт, для которого необходимо добавить новый необработанный контакт.
// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() { /* * Gets values from the UI */ String name = mContactNameEditText.getText().toString(); String phone = mContactPhoneEditText.getText().toString(); String email = mContactEmailEditText.getText().toString(); int phoneType = mContactPhoneTypes.get( mContactPhoneTypeSpinner.getSelectedItemPosition()); int emailType = mContactEmailTypes.get( mContactEmailTypeSpinner.getSelectedItemPosition());
В следующем фрагменте кода создается операция для вставки строки необработанного контакта в таблицу {@link android.provider.ContactsContract.RawContacts}:
/* * 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, mSelectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName()); // Builds the operation and adds it to the array of operations ops.add(op.build());
Затем код создает строки данных для строк отображаемого имени, телефона и адреса эл. почты.
Каждый объект компонента операции использует метод {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} для получения {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Ссылка возвращается обратно к объекту {@link android.content.ContentProviderResult} из первой операции, в результате чего добавляется строка необработанного контакта и возвращается его новое значение {@code android.provider.BaseColumns#_ID}. После этого каждая строка данных автоматически связывается по своему {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} с новой строкой {@link android.provider.ContactsContract.RawContacts}, которой она принадлежит.
Объект {@link android.content.ContentProviderOperation.Builder}, который добавляет строку адреса эл. почты, помечается флагом с помощью метода {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) withYieldAllowed()}, который задает предел:
// 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());
В последнем фрагменте кода представлен вызов метода {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} для вставки нового необработанного контакта и его строк данных.
// Ask the Contacts Provider to create a new contact Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" + mSelectedAccount.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); } }
С помощью пакетных операций можно также реализоватьоптимистическое управление параллелизмом. Это метод применения транзакций изменения без необходимости блокировать базовый репозиторий. Чтобы воспользоваться этим методом, необходимо применить транзакцию и проверить наличие других изменений, которые могли быть внесены в это же время. Если обнаружится, что имеется несогласованное изменение, транзакцию можно откатить и выполнить повторно.
Оптимистическое управление параллелизмом подходит для мобильных устройств, где с системой одновременно взаимодействует только один пользователь, а одновременный доступ к репозиторию данных нескольких пользователей — довольно редкое явление. Поскольку не применяется блокировка, экономится ценное время, которое требуется на установку блокировок или ожидания того, когда другие транзакции отменят свои блокировки.
Ниже представлен порядок использования оптимистического управления параллелизмом при обновлении одной строки {@link android.provider.ContactsContract.RawContacts}.
Если в промежутке между считыванием строки и попыткой ее изменения строка необработанного контакта была обновлена другой операцией, assert {@link android.content.ContentProviderOperation} завершится сбоем и в выполнении всего пакета операций будет отказано. Можно выбрать повторное выполнение пакета или выполнить другое действие.
В примере кода ниже демонстрируется, как создать assert {@link android.content.ContentProviderOperation} после запроса одного необработанного контакта с помощью {@link android.content.CursorLoader}.
/* * 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 mRawContactID = 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, mRawContactID); // Creates a builder for the assert operation ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(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<ContentProviderOperationg>; 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 }
С помощью отправки намерения в приложение для работы с контактами, которое имеется на устройстве, можно в обход получить доступ к поставщику контактов. Намерение запускает пользовательский интерфейс приложения на устройстве, посредством которого пользователь может работать с контактами. Такой тип доступа позволяет пользователю выполнять следующие действия:
Если пользователь вставляет или обновляет данные , вы можете сначала собрать эти данные, а затем отправить их вместе с намерением.
При использовании намерений для доступа к поставщику контактов посредством приложения для работы с контактами, имеющегося на устройстве, вам не нужно создавать собственный пользовательский интерфейс или код для доступа к поставщику. Также вам не нужно запрашивать разрешение на чтение или запись в поставщик. Приложение для работы с контактами, имеющееся на устройстве, может делегировать вам разрешение на чтение контакта, и поскольку вы вносите изменения в поставщик через другое приложение, вам не нужно разрешение на запись.
Более подробно общий процесс отправки намерения для получения доступа к поставщику описан в руководстве Основные сведения о поставщике контента в разделе «Доступ к данным посредством намерений». В таблице 4 ниже представлены сводные сведения об операциях, типе MIME и значениях данных, которые используются для доступных задач. Значения дополнительных данных, которые можно использовать для {@link android.content.Intent#putExtra(String, String) putExtra()}, указаны в справочной документации по {@link android.provider.ContactsContract.Intents.Insert}.
Задача | Действие | Данные | Тип MIME | Примечания |
---|---|---|---|---|
Выбор контакта из списка | {@link android.content.Intent#ACTION_PICK} |
Одно из следующего:
|
Не используется |
Отображение списка необработанных контактов или списка данных необработанного контакта (в зависимости от
предоставленного URI контента).
Вызовите метод
{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()},
который возвращает URI контента для выбранной строки. URI представлен в форме
URI контента таблицы, к которому добавлен |
Вставка нового необработанного контакта | {@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION} | Н/Д | {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE RawContacts.CONTENT_TYPE}, тип MIME для набора необработанных контактов. | Отображение экрана Добавить контакт в приложении для работы с контактами, которое установлено на устройстве. Отображаются значения дополнительных данных, добавленных вами в намерение. При отправке с помощью метода {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} URI контента нового добавленного необработанного контакта передается обратно в метод обратного вызова {@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()} вашей операции в аргументе {@link android.content.Intent} в поле data. Чтобы получить значение, вызовите метод {@link android.content.Intent#getData()}. |
Изменение контакта | {@link android.content.Intent#ACTION_EDIT} | {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} контакта. Операция редактора разрешит пользователю изменить любые данные, связанные с этим контактом. | {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE Contacts.CONTENT_ITEM_TYPE}, один контакт. | Отображение экрана редактирования контакта в приложении для работы с контактами. Отображаются значения дополнительных данных, добавленных вами в намерение. Когда пользователь нажимает на кнопку Готово для сохранения внесенных изменений, ваша операция возвращается на передний план. |
Отображение средства выбора, в котором также можно добавлять данные | {@link android.content.Intent#ACTION_INSERT_OR_EDIT} | Н/Д | {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} |
Это намерение также отображает экран средства выбора приложения для работы с контактами. Пользователь
может выбрать контакт для изменения или добавить новый контакт. В зависимости от выбора отображается экран редактирования или добавления контакта,
и отображаются дополнительные данные,
переданные вами в намерение. Если ваше приложение отображает такие данные контакта, как адрес эл. почты или номер телефона,
воспользуйтесь этим намерением, чтобы разрешить пользователю добавлять данные к существующему
контакту.
Примечание. Вам не нужно отправлять значение имени в дополнительные данные этого намерения, поскольку пользователь всегда выбирает существующее имя или добавляет новое. Кроме того, если отправить имя, а пользователь решит внести изменения, приложение для работы с контактами отобразит отправленное вами имя, перезаписав предыдущее значение. Если пользователь не заметит этого и сохранит изменения, старое значение будет утеряно. |
Приложение для работы с контактами, имеющееся на устройстве, не разрешает вам удалять необработанные контакты или любые его данные с помощью намерений. Вместо этого для обработки необработанного контакта используйте метод {@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} или {@link android.content.ContentProviderOperation#newDelete(Uri) ContentProviderOperation.newDelete()}.
Ниже представлен фрагмент кода для создания и отправки намерения, который вставляет новый необработанный контакт и его данные.
// Gets values from the UI String name = mContactNameEditText.getText().toString(); String phone = mContactPhoneEditText.getText().toString(); String email = mContactEmailEditText.getText().toString(); String company = mCompanyName.getText().toString(); String jobtitle = mJobTitle.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, mSelectedAccount.getType()); rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.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);
Поскольку в репозитории контактов содержится важная и конфиденциальная информация, которая, как ожидает пользователь, верна и актуальна, в поставщике контактов предусмотрены четко определенные правила для обеспечения целостности данных. При изменении данных контактов вы обязаны соблюдать эти правила. Вот наиболее важные из этих правил:
Создав и используя собственные типы MIME, вы можете вставлять, изменять, удалять и извлекать в таблице{@link android.provider.ContactsContract.Data} собственные строки данных. Ваши строки ограничены использованием столбца, который определен в {@link android.provider.ContactsContract.DataColumns}, однако вы можете сопоставить ваши собственные имена столбцов по типам строк с именами столбцов по умолчанию. Данные для ваших строк отображаются в приложении для работы с контактами, которое имеется на устройстве, однако их не удастся изменить или удалить, а также пользователи не смогут добавить дополнительные данные. Чтобы разрешить пользователям изменять ваши настраиваемые строки данных, необходимо реализовать в вашем приложении операцию редактора.
Для отображения настраиваемых данных укажите файл contacts.xml
, содержащий элемент
<ContactsAccountType>
и один или несколько его
<ContactsDataKind>
дочерних элементов. Дополнительные сведения об этом представлены в разделе
<ContactsDataKind> element
.
Дополнительные сведения о настраиваемых типах MIME представлены в руководстве Создание поставщика контента.
Поставщик контактов разработан специально для обработки операций синхронизации данных контактов между устройством и службой в Интернете. Благодаря этому пользователи могут загружать существующие данные на новое устройство и отправлять их в новый аккаунт. Синхронизация также обеспечивает предоставление пользователю всегда актуальных сведений, независимо от источника их добавления и внесенных в них изменений. Еще одно преимущество синхронизации заключается в том, что данные контактов доступны даже в том случае, если устройство не подключено к сети.
Несмотря на множество различных способов реализации синхронизации данных, в системе Android имеется подключаемая платформа синхронизации, которая позволяет автоматизировать выполнение следующих задач:
Для использования этой платформы вы предоставляете подключаемый модуль адаптера синхронизации. Каждый адаптер синхронизации является уникальным для службы и поставщика контента, однако способен работать с несколькими аккаунтами в одной службе. В платформе также предусмотрена возможность использовать несколько адаптеров синхронизации для одной и той же службы или поставщика.
Адаптер синхронизации реализуется как подкласс класса {@link android.content.AbstractThreadedSyncAdapter} и устанавливается в составе приложения Android. Система узнает о наличии адаптера синхронизации из элементов в манифесте вашего приложения, а также из особого файла XML, на который имеется указание в манифесте. В файле XML определяются тип аккаунта в онлайн-службе и центр поставщика контента, которые вместе служат уникальными идентификаторами адаптера. Адаптер синхронизации находится в неактивном состоянии до тех пор, пока пользователь не добавит тип аккаунта и не включит синхронизацию с поставщиком контента. На этом этапе система вступает в управление адаптером , при необходимости вызывая его для синхронизации данных между поставщиком контента и сервером.
Примечание. Использование типа аккаунта для идентификации адаптера синхронизации
позволяет системе обнаруживать и группировать адаптеры синхронизации, которые обращаются к разным службам
из одной и той же организации. Например, у всех адаптеров синхронизации для онлайн-служб Google
один и тот же тип аккаунта — com.google
. Когда пользователи добавляют на свои устройства аккаунт Google, все
установленные адаптеры синхронизации группируются вместе; каждый из адаптеров
синхронизируется только с отдельным поставщиком контента на устройстве.
Поскольку в большинстве служб пользователям сначала необходимо подтвердить свою подлинность, прежде чем они смогут получить доступ к данным, система Android предлагает платформу аутентификации, которая аналогична платформе адаптера синхронизации и зачастую используется совместно с ней. Платформа аутентификации использует подключаемые структуры проверки подлинности, которые представляют собой подклассы класса {@link android.accounts.AbstractAccountAuthenticator}. Такая структура проверяет подлинность пользователя следующим образом:
Если служба приняла учетные данные, структура проверки подлинности может сохранить их для использования в дальнейшем. Благодаря использованию структур проверки подлинности {@link android.accounts.AccountManager} может предоставить доступ к любым маркерам аутентификации, которые поддерживает структура проверки подлинности и которые она решает предоставить, например, к маркерам аутентификации OAuth2.
Несмотря на то что аутентификация не требуется, она используется большинством служб для работы с контактами. Тем не менее, вам не обязательно использовать платформу аутентификации Android для проверки подлинности.
Чтобы реализовать адаптер синхронизации для поставщика контактов, начните с создания приложения Android, которое содержит следующие компоненты.
В
примере адаптера синхронизации именем класса этой службы является
com.example.android.samplesync.syncadapter.SyncService
.
В
примере адаптера синхронизации адаптер определяется в классе
com.example.android.samplesync.syncadapter.SyncAdapter
.
В
примере адаптера синхронизации именем класса этой службы является
com.example.android.samplesync.authenticator.AuthenticationService
.
В
примере адаптера синхронизации структура проверки подлинности определяется в классе
com.example.android.samplesync.authenticator.Authenticator
.
<service>
в манифесте приложения. Эти элементы
включают дочерние элементы
<meta-data>
, в которых имеются определенные данные для
системы.
<meta-data>
для службы адаптера синхронизации указывает на файл
XML res/xml/syncadapter.xml
. В свою очередь, в этом файле задается
URI веб-службы для синхронизации с поставщиком контактов,
а также тип аккаунта для этой веб-службы.
<meta-data>
для структуры проверки подлинности указывает на файл XML
res/xml/authenticator.xml
. В свою очередь, в этом файле задается
тип аккаунта, который поддерживает структура проверки подлинности, а также ресурсы пользовательского интерфейса,
которые отображаются в процессе аутентификации. Тип аккаунта, указанный в этом
элементе, должен совпадать с типом аккаунта, который задан для
адаптера синхронизации.
Для управления входящими данными из социальных сетей используются таблицы {@code android.provider.ContactsContract.StreamItems} и {@code android.provider.ContactsContract.StreamItemPhotos}. Можно создать адаптер синхронизации, который добавляет поток данных из вашей собственной сети в эти таблицы, либо вы можете считывать поток данных из этих таблиц и отображать их в собственном приложении. Можно также реализовать оба этих способа. С помощью этих функций вы можете интегрировать службы социальных сетей в компоненты Android для работы с социальными сетями.
Элементы потока всегда ассоциируются с необработанным контактом. Идентификатор
{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} связывается со значением
_ID
необработанного контакта. Тип аккаунта и его имя для необработанного
контакта также хранятся в строке элемента потока.
Данные из потока следует хранить в следующих столбцах:
Для отображения идентификационной информации для элементов потока воспользуйтесь {@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}, {@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} и {@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} для связывания с ресурсами в вашем приложении.
В таблице {@code android.provider.ContactsContract.StreamItems} также имеются столбцы {@code android.provider.ContactsContract.StreamItemsColumns#SYNC1}—{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4}, которые предназначены исключительно для адаптеров синхронизации.
Фотографии, связанные с элементом потока, хранятся в таблице {@code android.provider.ContactsContract.StreamItemPhotos}. Столбец {@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} в этой таблице связан со столбцом {@code android.provider.BaseColumns#_ID} в таблице {@code android.provider.ContactsContract.StreamItems}. Ссылки на фотографии хранятся в следующих столбцах таблицы:
Эти таблицы работают аналогично другим основным таблицам в поставщике контактов, за исключением указанных ниже моментов.
null
. Запрос возвращает
объект Cursor, в котором содержится одна строка с одним столбцом
{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}.
Класс {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} определяет дочернюю таблицу объектов {@code android.provider.ContactsContract.StreamItemPhotos}, в которой содержатся строки для одного элемента потока.
Управление потоком данных из социальных сетей осуществляется поставщиком контактов совместно с приложением для управления контактами, имеющимся на устройстве. Такой подход позволяет организовать эффективное использование данных из социальных сетей с данными о существующих контактах. Доступны указанные ниже функции.
Регулярная синхронизация элементов потока с помощью поставщика контактов выполняется так же, как и любая другая синхронизация. Дополнительные сведения о синхронизации представлены в разделе Адаптеры синхронизации поставщика контактов. Регистрация уведомлений и приглашение контактов рассматриваются в следующих двух разделах.
Чтобы зарегистрировать адаптер синхронизации для получения уведомлений о просмотрах пользователями контакта, управление которым осуществляется вашим адаптером синхронизации, выполните указанные ниже действия.
res/xml/
своего проекта создайте файл
contacts.xml
. Если у вас уже есть этот файл, переходите к следующему действию.
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
.
Если этот элемент уже существует, можете переходить к следующему действию.
viewContactNotifyService="serviceclass"
к элементу, где
serviceclass
— это полное имя класса службы,
которая должна получить намерение из приложения для работы с контактами. Для службы-уведомителя
используйте класс, который является расширением класса {@link android.app.IntentService}, чтобы разрешить службе
получать намерения. Данные во входящем намерении содержат URI контента необработанного
контакта, выбранного пользователем. В службе-уведомителе можно привязать адаптер синхронизации, а затем вызвать его
для обновления данных для необработанного контакта.
Чтобы зарегистрировать операцию, которую следует вызвать при выборе пользователем элемента потока или фотографии (или обоих элементов), выполните указанные ниже действия.
res/xml/
своего проекта создайте файл
contacts.xml
. Если у вас уже есть этот файл, переходите к следующему действию.
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
.
Если этот элемент уже существует, можете переходить к следующему действию.
viewStreamItemActivity="activityclass"
к элементу, где
activityclass
— это полное имя класса операции,
которая должна получить намерение из приложения для работы с контактами.
viewStreamItemPhotoActivity="activityclass"
к элементу, где
activityclass
— это полное имя класса операции,
которая должна получить намерение из приложения для работы с контактами.
Дополнительные сведения об элементе <ContactsAccountType>
представлены в разделе
элемент <ContactsAccountType>.
Данные во входящем намерении содержат URI контента элемента или фотографии, выбранных пользователем. Чтобы использовать разные операции для текстовых элементов и фотографий, используйте оба атрибута в одном файле.
Пользователям не обязательно выходить из приложения для работы с контактами, которое имеется на устройстве, чтобы пригласить контакт на сайт социальной сети. Вместо этого приложение для работы с контактами может отправить намерение для приглашения контакта в одну из ваших операций. Для этого выполните указанные ниже действия.
res/xml/
своего проекта создайте файл
contacts.xml
. Если у вас уже есть этот файл, переходите к следующему действию.
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
.
Если этот элемент уже существует, можете переходить к следующему действию.
inviteContactActivity="activityclass"
;inviteContactActionLabel="@string/invite_action_label"
.
activityclass
представляет собой полное имя класса операции,
которая должна получить намерение. Значениеinvite_action_label
— это текстовая строка, которая отображается в меню Добавить подключение
в приложении для работы с контактами.
Примечание. ContactsSource
— это устаревшее имя тега для
ContactsAccountType
, которое больше не используется.
В файле contacts.xml
содержатся элементы XML, которые управляют взаимодействием вашего
адаптера синхронизации и вашего приложения с поставщиком контактов и приложением для работы с контактами. Эти
элементы описаны в следующих разделах.
Элемент <ContactsAccountType>
управляет взаимодействием вашего
приложения с приложением для работы с контактами. Ниже представлен его синтаксис.
<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">
находится в:
res/xml/contacts.xml
может содержать:
<ContactsDataKind>
Описание
Этот элемент объявляет компоненты и элементы пользовательского интерфейса, с помощью которых пользователи могут приглашать свои контакты в социальную сеть, уведомлять пользователей при обновлении одного из их потоков данных из социальных сетей и др.
Обратите внимание, что префикс атрибута android:
необязательно использовать для атрибутов
<ContactsAccountType>
.
Атрибуты
NotifierService.java
в
образце приложенияSampleSyncAdapter.
Например, если вы установили приложение Google+ на ваше устройство и выполняете синхронизацию данных в Google+ с приложением для работы с контактами, то круги Google+ будут обозначены в приложении для работы с контактами как группы на вкладке Группы. При нажатии на на круг Google+ участники крга отобразятся как группа контактов. В верхней части экрана находится значок Google+; если нажать на него, управление перейдет в приложение Google+. В приложении для управления контактами это реализовано с помощью {@code viewGroupActivity}, в которой значок Google+ используется в качестве значения {@code viewGroupActionLabel}.
Для этого атрибута можно использовать идентификатор строкового ресурса.
Элемент <ContactsDataKind>
управляет отображением настраиваемых строк данных вашего
приложения в интерфейсе приложения для работы с контактами, которое имеется на устройстве. Ниже представлен его синтаксис.
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
находится в:
<ContactsAccountType>
Описание
Используйте этот элемент для отображения содержимого настраиваемой строки данных в приложении для работы с контактами как части
сведений о необработанном контакте. Каждый дочерний элемент <ContactsDataKind>
элемента <ContactsAccountType>
представляет собой тип настраиваемой строки данных, который
адаптер синхронизации добавляет в таблицу {@link android.provider.ContactsContract.Data}. Для каждого используемого вами настраиваемого типа MIME необходимо добавить один элемент
<ContactsDataKind>
. Вам не нужно добавлять элемент
, если у вас имеется настраиваемая строка данных, для которой не требуется отображать данные.
Атрибуты
vnd.android.cursor.item/vnd.example.locationstatus
может быть настраиваемым
типом MIME для строки данных, в которой находятся записи о последнем известном местоположении контакта.
Помимо основных функций, описанных разделах выше, в поставщике контактов предусмотрены указанные ниже полезные функции для работы с данными контактов.
Поставщик контактов может дополнительно отметить коллекции связанных контактов с данными о группе. Если серверу, который связан с учетной записью пользователя, требуется сохранить группы, адаптеру синхронизации для типа этого аккаунта следует передать данные о группах из поставщика контактов на сервер. При добавлении пользователем нового контакта на сервер и последующем помещении этого контакта в новую группу адаптер синхронизации должен добавить эту новую группу в таблицу {@link android.provider.ContactsContract.Groups}. Группа или группы, в которые входит необработанный контакт, хранятся в таблице {@link android.provider.ContactsContract.Data} с использованием типа MIME {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}.
Если необходимо создать адаптер синхронизации, который будет добавлять данные необработанного контакта с сервера в поставщик контактов, а вы не используете группы, то вам необходимо указать для поставщика, чтобы он сделал ваши данные видимыми. В коде, который выполняется при добавлении пользователем аккаунта на устройство, обновите строку {@link android.provider.ContactsContract.Settings}, которую поставщик контактов добавляет для этого аккаунта. В этой строке укажите в столбце {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE Settings.UNGROUPED_VISIBLE} значение «1». После этого поставщик контактов всегда будет делать ваши данные видимыми, даже если вы не используете группы.
В таблице {@link android.provider.ContactsContract.Data} хранятся фотографии в виде строк {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE Photo.CONTENT_ITEM_TYPE} типа MIME. Столбец {@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} в строке связан со столбцом {@code android.provider.BaseColumns#_ID} необработанного контакта, которому он принадлежит. Класс {@link android.provider.ContactsContract.Contacts.Photo} определяет вложенную таблицу {@link android.provider.ContactsContract.Contacts}, в которой содержится информация об основной фотографии контакта (которая является основной фотографией основного необработанного контакта этого контакта). Аналогичным образом класс {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} определяет вложенную таблицу {@link android.provider.ContactsContract.RawContacts}, в которой содержится информация об основной фотографии необработанного контакта.
В справочной документации по {@link android.provider.ContactsContract.Contacts.Photo} и {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} содержатся примеры получения информации о фотографии. К сожалению, отсутствует класс для удобного извлечения миниатюры основной фотографии необработанного контакта, однако вы можете отправить запрос в таблицу {@link android.provider.ContactsContract.Data}, выбрать {@code android.provider.BaseColumns#_ID} необработанного контакта, {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE Photo.CONTENT_ITEM_TYPE} и столбец {@link android.provider.ContactsContract.Data#IS_PRIMARY}, чтобы найти строку основной фотографии необработанного контакта.
Потоки данных из социальных сетей также могут включать фотографии. Они находятся в таблице {@code android.provider.ContactsContract.StreamItemPhotos}, дополнительные сведения о которой представлены в разделе Фотографии из потока данных из социальных сетей.