Идентификаторы контакта
Итак, у контакта есть два идентификатора. ContactID представляет из себя обычное число, типа long: 2, 40, 3222 и т.д. LookupKey - это кодированная строка типа "1157icbbec86124b1b50", "30410abc...gmail.com" и т.д.Если вам известен хотя бы один идентификатор, то вы можете получить URI контакта и, через URI, запросить любую информацию о контакте. Вот как это делается:
//Вариант 1: известен contactID Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId)); //Вариант 2: известен lookupKey Uri uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey) //Вариант 3: известны оба идентификатора Uri uri = getLookupUri(contactId, lookupKey) //вариант 3 не работает под 2.1 из-за бага андроида //Пример: находим имя контакта Cursor c = getContentResolver().query(uri, new String[]{Contacts.DISPLAY_NAME}, null, null, null); try { c.moveToFirst(); String displayName = c.getString(0); } finally { c.close(); }О том, как правильно использовать Contact URI (и Contact API в целом) можно почитать, например, здесь и здесь.
Первые два варианта подходят, когда вам известен один из идентификаторов. Третий вариант - когда известны оба (в 2.1 он не работает). Вопрос - зачем нам вариант три, когда первых двух вроде бы достаточно? Вариант 3 нужен потому, что идентификаторы контакта могут изменяться с течением времени.
Непостоянство contactId и lookupKey
ContactId может измениться при агрегации контактов. Скажем, есть у вас в контактах пользователь Вася, contactId = 1. Вы установили на свой телефон skype. В скайпе Вася у вас тоже есть, на телефоне появляется контакт Вася(2) с contactId = 100. Андроид автоматически объединяет эти контакты в общий, агрегированный контакт Вася(3) с contactId = 200. Если после агрегации вы попробуете найти контакт Вася(1) с contactId = 1, то вы его не найдете. Контакт потерялся.Чтобы избежать такой потери контактов, разработчики Android и ввели lookupkey. Если вы вместе с contactId=1 сохранили второй идентификатор контакта lookupkey="abc", то используя вариант поиска номер 3, вы без проблем найдете агрегированный контакт Васи:
Uri uri = getLookupUri(1, "abc");Здесь есть тонкий момент. Идентификатор lookupKey может изменяться. Как правило он изменяется после редактирования свойств контакта. Так что в SQL-запросах к Contact API никогда нельзя включать явные выражения типа " and (LOOKUP_KEY='abc')" - будет работать, но до поры до времени. Lookup Key нужно передавать в getLookupUri, получать Uri, а дальше работать c Uri и contactId, не используя более lookupKey.
Если lookupKey изменился - можно ли будет по нему найти контакт? Практика показывает, что можно - устаревшие lookupKey работают корректно.
Как ссылаться на контакт?
Мы подходим к вопросу - если требуется сохранить ссылку на контакт, какой идентификатор сохранять? Одного contactId однозначно не хватает. Можно ли обойтись одним lookupKey?В документации написано буквально следующее:
If performance is a concern for your application, you might want to store both the lookup and the long ID of a contact and construct a lookup URI out of both IDs...
When both IDs are present in the URI, the system will try to use the long ID first. That is a very quick query. If the contact is not found, or if the one that is found has the wrong lookup key, the content provider will parse the lookup key and track down the constituent raw contacts. If your app bulk-processes contacts, you should maintain both IDs. If your app works with a single contact per user action, you probably don't need to bother with storing the long ID.
Другими словами, для однозначной идентификации контакта достаточно хранить один лишь lookupKey. На самом деле, это не так.
Неоднозначность LookupKey
Мое приложение Animated Contact Widget предназначено для быстрого доступа к контактам. Естественно, ему приходится хранить ссылки на контакты. Вплоть до текущей версии в информации о контакте хранился единственный идентификатор - lookupKey. В 99% случаев все работало без проблем. Но время от времени приходили единичные письма от пользователей, которые сообщали о странном баге - выбираешь один контакт, а виджет создается для другого...В чем дело я не мог понять очень долго. На stackoverflow упоминался аналогичный баг, но дельного ответа на него никто не дал.
Наконец нашелся пользователь, который не поленился и помог мне отыскать причину проблемы (за что ему огромнейшее спасибо). Отладочные логи показали, что на девайсе пользователя примерно треть контактов имеет ОДИНАКОВЫЕ lookupKey. Что же удивляться, что контакт выбирается не тот... Вот фрагмент лог файла:
k=1957i5 c=152
k=1957i5 c=153
k=1957i46d39cd0743de699 c=154
k=1957i327657105acc3975 c=155
k=1957i5 c=394
k=1957i300808f8df4189b1 c=156
k=1957i660d8a742c21a4a6 c=411
k=1957i3793876d5c49591e c=157
k=1957i5 c=158
k=1957i250bebb5bd1009fd c=170
k=1957i39112193df972581 c=171
k=1957i5 c=431
k=1957i2c74309cd7539f2b c=169
Здесь c - contactId, k - lookupKey.
Трудно судить, насколько часто такая проблема проявляется. Сообщений о баге я получил с десяток. Плюс, наверное, было десятка два комментариев на маркете. Из полмиллиона закачек - это мизер. Тем не менее, сообщения об этой ошибке приходили стабильно, от пользователей с разными устройствами. Проблема точно проявлялась на некоторых HTC-девайсах (Desire, Wildfire S).
Мы попробовали выявить общий знаменатель между "бракованными" контактами. Не получилось. Есть подозрение, что шалит синхронизация HTC Sense - Outlook, но не факт, что дело в ней...
Начиная с версии 1.5.5, Animated Widget использует два идентификатора контакта для ссылки на контакт. Баг пофиксился.
Здравствуйте. Если getLookupUri(...) гарантированно возвращает Uri контакта по двум параметрам, может быть стоит именно его и сохранять для дальнейшей работы?
ОтветитьУдалитьСпасибо, ваша статья прояснила многое
ОтветитьУдалить