Как изменить ID Android?
Android ID — это такой идентификатор, который позволяет приложениям определять смартфон среди других гаджетов. Часто используется для привязки аккаунтов к определенному смартфону, чтобы ограничить количество регистраций с одного устройства и многое другое. Некоторые задаются вопросом, а как изменить ID на Андроид, чтобы убрать эти ограничения? В этом материале мы и рассмотрим два способа изменить этот идентификатор.
Как изменить ID Android?
Существуют два способа изменить ID у смартфонов. Для первого способа требуются Root-права. Чтобы их получить, необходимо проделать некоторые действия. Однако мы не будем рассматривать этот вопрос в данном материале, а лишь покажем приложение, которое работает при наличии таких прав.
Называется это приложение «Device ID Changer» и доступно для скачивания в магазине Google Play. Для повседневного использования достаточно и бесплатной версии утилиты:
Преимущество приложения в том, что оно может изменить ID устройства только для определенного приложения. Для этого достаточно выбрать нужное приложение из выпадающего списка и нажать на «Apply».
Обратите внимание на то, что после изменения ID необходимо перезагрузить операционную систему. Для этого удерживайте кнопку питания и выберите соответствующий пункт меню.
Второй способ изменения ID — это полный сброс настроек смартфона. В этом случае все файлы, фотографии, видео и приложения будут удалены. Рекомендуем делать это действие только после резервного копирования данных на другое устройство. Инструкция по сбросу настроек:
- Открываем настройки.
- Находим раздел «Система», переходим в него.
- Ищем подраздел «Сброс настроек» и заходим туда.
- Ищем пункт «Сбросить к заводским настройкам» (на разных версиях Android данный пункт может называться по разному) и нажимаем на него
Ждем удаления всех данных и автоматической перезагрузки устройства. Заново заходим в свои учетные записи и готово — ваш смартфон получил новый Android ID.
Источник
GUID-подобные первичные ключи в SQLite на Android
Интро
Каждая таблица в SQLite по умолчанию содержит приватный ключ на основе автоматически генерируемого 64-битного целого. Это эффективно и удобно в большинстве ситуаций. Неудобства начинаются, пожалуй, только в двух случаях:
- когда диапазона 64 бит не хватает (тогда стоит задуматься о целесообразности SQLite задаче)
- когда хранилище становится «распределенным»
Может показаться, что и второй задачи в комбинации с SQLite не должно возникать, но распределенность не всегда означает что-нибудь вроде BigData. Типичный пример (из-за чего лично мне и понадобилось исследование на эту тему) это приложение с возможностью синхронизации данных между устройствами. Это может быть как что-то небольшое, как записная книжка, так и более нагруженное, как история браузера. Проблемой тут становится не столько объем данных, сколько слияние нескольких баз. Очевидно, что целочисленные счетчики записей, начинающие отсчет с 1, неизбежно будут выдавать конфликтующие последовательности, а значит использовать их в качестве уникального идентификатора записи на нескольких устройствах уже нельзя. Можно заморочиться с разделением на поддиапазоны или «сдвиганием» айдишников записей перед их передачей, но это все кривые и хрупкие костыли. Никто так не делает, конечно же. Вместо этого каждое устройство присваивает своим записям что-нибудь вроде GUID-а – просто и надежно.
GUID в качестве первичного ключа
GUID это случайное «число», длиной в 128 бит. То есть в БД это будет 16 байт в виде BLOB-а, либо минимум 32 байта в виде строки. Определенный оверхед (особенно, если остальные столбцы небольшие) по сравнению с дефолтным ключом, который хранится очень эффективно: обычно там не 8 байт, а столько, сколько требует для представления значение ключа. Этот оверхед ради решения задачи мы готовы платить, но не хотим усугублять – поэтому конечно же предпочитаем хранение в бинарном виде, а не текстовой строкой.
Что ж, объявить столбец с блобом несложно, сделаем примитивную табличку:
Можно еще добавить WITHOUT ROWID в качестве спецификатора таблицы для оптимизации – чтобы SQLite не добавляла и не поддерживала неявный ключевой столбец.
Тему можно было бы закрывать, если бы не желание заставить БД самостоятельно генерировать айдишники, так же, как в случае с дефолтным целочисленным ключом. Что ж, если нет принципиального требования иметь настоящие GUID (которые не совсем просто рандомное число, а имеют несколько предопределенных бит), то это тоже легко:
На этом можно было бы вновь остановиться, если бы не перфекционизм. Забегая вперед, скажу, что даже с перфекционизмом тяжелой формы на этом и стоит остановиться, если вы не собираетесь вставлять в БД миллионы записей. А вот если может быть так, что собираетесь, то стоит обратить внимание, что длинное случайное число не очень хорошо подходит в качестве ключа по той причине, что его дороже индексировать: просто складывать записи одну за другой и надеяться на быстрый поиск уже не выйдет.
Проблема эта к счастью давно и успешно решена и решение адаптировано на многие БД. Вкратце там все просто: первые 6 байт идентификатора заменяются на timestamp. В результате записи создаются сразу (частично) упорядоченными, что сильно облегчает их индексацию. Вероятность коллизий при этом увеличивается, но незначительно. И снова история закончилась бы ровно на этом месте, если бы в Android-ном API SQLiteDatabase позволяла определять внешние функции, чтобы сгенерировать гуидоподобный BLOB. Можно конечно генерить их и в Java-коде и биндить ко всем запросам на вставку, но это неспортивно. Кроме того, могут быть другие причины не делать этого. Например необходимость держать «глобальные» идентификаторы отдельно от «локальных», генерируя их по мере необходимости при помощи триггера.
Ну хорошо, взять 6 байт от unix timestamp с грехом пополам можно так:
Результатом будет число. Например, такое: 1489877740453 – на момент написания статьи. Хорошие новости в том, что оно обычно будет неубывающим, и его можно считать средствами самой БД. Но дальше начинаются некоторые сложности. Дело в том, что в SQLite очень ограниченный набор функций для работы с BLOB: только обрезать ( substr() ) и склеить ( || ). И как заставить интерпретировать число в качестве строки байтов, непонятно. То есть можно конечно сделать CAST(. AS BLOB) , но это не то: оно переведет число в строку, а потом возьмет байты полученной строки – то есть превратит 6 байт в 13. Даже если предварительно отформатировать в шестнадцатиричное представление, будет много – 12. Не катит.
Преобразование числа в BLOB
… в SQLite невозможно – ответит вам google и stackoverflow. Так-то оно, конечно, правда, но если очень хочется, то вообще-то можно. В Интернете мне ничего найти не удалось, и пришлось изобрести самому. Сразу скажу: это будет грязно 🙂
Итак, у нас есть склейка ( || ), значит, имея две байтовые строки – timestamp и случайную часть – мы могли бы получить COMB Джимми Нельсона:
Искомые ts_bytes это всего лишь строка из 6 байт, представляющая целое число. Давайте еще раз взглянем на него: 1489877740453 . Или 0x 01 5A E3 A2 2B A5 . Если бы могли взять по отдельности каждый байт в виде BLOB-а, и склеить их вместе – даже в ручном режиме это всего (и всегда) 6 склеек. Что ж, попробуем разделить число на байты. Их числовые значения можно получить при помощи небольшой арифметики:
- первый: ts >> 40 == 1 (0x01)
- второй: (ts >> 32) % 256 == 90 (0x5A)
- третий: (ts >> 24) % 256 == 227 (0xE3)
- .
Но, опять же, это пока не байты. Интерпретатор SQLite будет считать это просто числами:
А нам нужен BLOB. BLOB из одного байта, представляющего полученное число. Явно мы это сделать не можем, но если бы у нас было что-то вроде таблицы – значений байта-то всего 256 штук. Тут мы вспоминаем о второй операции, доступной в SQLite и возвращающей байты – substr , которая по индексу возвращает подстроку букв или байт. Бинго! Захардкодим все значения байта в строку, где индексом само значение этого байт и будет. К счатью, можно записывать бинарный литерал при помощи синтаксиса вида x’DEADBEEF’:
Это слегка псевдокод, потому что переносить строки так нельзя, но так нагляднее, а в коде можно и в одну строку отформатировать. Зато теперь все, что осталось сделать, это «вырезать» нужный байт из таблицы и склеить с другими. Шесть раз:
Псевдо-GUID в бинарной форме готов! На самом деле SQLite пока что будет считать получаенную строку байт «текстом», но CAST(. AS BLOB) сделает все, как надо. Вообще-то это даже обязательно, потому что иначе чтение из этого столбца будет возвращать не 16 байт, как ожидается, а 17 – с нулевым терминатором строки. Осталось подставить выражение в качестве значения столбца по умолчанию.
Автовставка идентификаторов
Просто запихнуть весь этот «поезд» внутрь DEFAULT(. ) в определении столбца таблицы нельзя, потому что там должны быть только «простые» выражения, а нам нужны вложенные SELECT-ы, чтобы избежать копипасты и множественных вычислений одного и того же.
К счастью, в SQLite есть триггеры, с помощью которых можно модифицировать строки на лету при вставке. К сожалению, ни фаза BEFORE INSERT , ни AFTER INSERT не подходят для обслуживания PRIMARY KEY , т.к. для удовлетворения неявного условия NOT NULL значение столбца нужно обязательно указывать в изначальном запросе. К тому же, для таких триггеров UPDATE-выражение снова позволяет только примитивные выражения. Зато доступен тип триггеров INSTEAD OF INSERT , который как раз может создать новую запись на основе переданных значений с добавлением сгенерированного блоба. Есть с ним только одна особенность, не указанная в документации: триггер INSTEAD OF INSERT нельзя создать на таблицу. Можно только на VIEW.
В итоге схема выстраивается следующая:
- основная таблица явно используется только для чтения
- запросы на запись идут во VIEW-пустышку с триггером на вставку
- триггер генерирует BLOB и делает вставку в основную таблицу
Читаем как обычно:
А записываем так:
Можно было положить таблицу байт в отдельную VIEW ради удобочитаемости, но это несколько негативно сказывается на производительности. Также можно было оставить первичный ключ на целоцисленном счетчике, сделать столбец guid просто уникальным и написать триггер ON AFTER INSERT , которы бы «передобавлял» строку с новым guid , но, забегая чуть вперед, скажу, что это медленнее примерно на 30%. Кстати, самое время посмотреть на производительность.
Производительность
Очевидно, что склейка байтов вручную медленнее встроенной функции randomblob() . Выигрыш должен появиться на большом количестве вставок. Проведем замеры. Сравнивать будем «обычный» целочисленный ROWID, ключ на основе randomblob(16) и наши частично упорядоченные блобы (COMBs, как их назвали в вышеупомянутой статье).
Тестовый сценарий таков:
- три транзакции вставок по миллиону записей
- случайная выборка всех вставленных записей по id
Время записи замеряется как для каждой серии, так и внутри через каждые 20% записей. Тесты запускались в эмуляторе Android 6.0 (SQLite 3.8.10). Исходники тут.
На графике: время вставки каждой последующей порции из 200 тысяч записей. Эталон производительности, понятное дело, дефолтный целочисленный индекс (синяя линия). Его скорость не зависит от количества последовательных вставок. Желтная линия (COMBs) это наш пациент. Его скорость также практически постоянна, хотя и ниже на 55-59%. А красная линия это таблица с первичным ключом на randomblob(16). Видно, что, начиная всего на 11% медленнее INTEGER PRIMARY KEY, где-то после первого миллиона вставок ее накладные расходы на поддержание индекса превышают частично упорядоченные последовательности и продолжают расти, достигая 75% замедления к концу 3го миллиона.
На самом деле COMB можно сделать еще быстрее. Текущая проблема заключается в том, что с миллисекундной точностью временных отметок соседние строки выстраиваются в кластеры по 18-20 штук, где первые 6 байт (таймстамп) – одинаковые, частично возвращая проблему упорядочивания случайных байт. Если к таймстампу каким-то образом прибавлять порядковый номер добавляемой записи (хотя бы внутри транзакции), это снизит оверхед до 29-34% по сравнению с «INT» и даст выигрыш по сравнению с randomblob(16) уже после 500 тысяч записей.
Недостаток в том, что в простейшем случае порядковый номер нужно передавать из управляющего кода, а этого делать не хочется по условиям задачи. Кроме того, уже и так можно сделать выводы.
Выводы
SQLite сама по себе очень неплохо управляется с индексированием даже чего-то GUID-о-подобного.
Если предполагаемый объем данных не превышает хотя бы 500 тысяч записей, чистый randomblob() обладает вполне приемлемой производительностью. Я в своем текущем проекте поэтому скорее всего его и выберу.
Даже если записей будет много, но вставляются они редко или, тем более, в виде единичных записей, тип первичного ключа не будет играть вообще никакой роли в производительности. Одна только фиксация транзакции (в Android с дефолтными настройками БД) занимает порядка 20-50 миллисекунд. И в разы больше, если система IO поднагружена. Вставка записи внутри массовой транзакции, которая происходит за микросекунды, по сравнению с этим занимает ничтожное время при любом раскладе.
В SQLite можно превращать числа в BLOB – было бы желание 🙂
Источник
Как поменять УникальныйИдентификатор (GUID) у объектов?
1. Запускаешь «Поиск и замена дублирующих элементов спаравочников и документов» в одной из баз — выбираешь (атоматом) нужный элемент и менаешь на него все ссылки от второго, но второй не удаляеш, а только помечаеши на удаление. Делаеши обмен (можно только помеченного на удаление, но межно и всех ссылающихся на него объектос из обмена). Во второй базе принимаешь обмен — один из объектов становится также помеченным на удаление, запускаеши в ней «Поиск и замена дублирующих элементов спаравочников», но в качестве «правильного» выбираешь не по числу ссылок, а тот, который без пометки — удаляешь дубли. После этого удаляешь в каждой из баз помеченный. Может не получиться с некоторыми канстантами, используемыми в глобальный настройках — ихменяешь обработкой типа:
Об = Константы.ВалютаУправленческогоУчета.СоздатьМенеджерЗначения();
Об.ОбменДанными.Загрузка = Истина;
Об.Значение = .
Об.Записать();
где значение выбираешь из запроса
Запрос = Новый Запрос;
Запрос.Текст = »
|ВЫБРАТЬ
| Валюты.Ссылка
|ИЗ
| Справочник.Валюты КАК Валюты
|ГДЕ
| Валюты.ПометкаУдаления = ЛОЖЬ
| И Валюты.Наименование = &Наименование
|»;
Запрос.УстановитьПараметр(«Наименование», «руб.»);
Результат = Запрос.Выполнить().Выбрать();
Результат.Следующий();
Значение = Результат.Ссылка;
Ключевое тут Об.ОбменДанными.Загрузка = Истина; для отключение внутренних проверок типа «Объект не может быть изменен т.к. используется где-то глобально»
А потом уже удалить окончательно.
Можно узать аналогичные обработки типа «Замена ссылок» — схема работы аналогична, только предварительно проверить, работают ли они и с регистрами.
2. Править «Регистр соответствий для обмена» — для нужной номенклатуры указав «правильный» УИД (если обмены работают через него). Генератор строк приаттачен.
Делал для решения аналогичногой проблемы.
3. Если начальной синхронизации еще не было, то надо перенастроить настройки обмена в «Ковертации данный» — для нужного спрвочника в «конвертация свойств» задать поиск соответствующего по наименованию, а не по УИД
Уважаемый автор, у вас модель обмена данными спроектирована не правильно, если у вас возникают проблемы подобного рода. ИСПОЛЬЗУЙТЕ КОНВЕРТАЦИЮ ДАННЫХ (как писали выше уважаемые «dvv01» и «Region102»), выдергивайте правила обмена, дорабатывайте для правильной синхронизации и используйте. За многолетнюю практику всевозможных обменов проблемы возникали только в случае когда бездумно используется типовой обмен. Прописывать гуиды в чистом виде это необдуманно, вы же не ком объекты пишите где гуид уникален в принципе всегда, поэтому такой подход не всегда уместен, в вашем случае это точно лишнее, проще подвязаться к коду валюты и наименованию.
Подмена гуида SQLем возможна, в этом нет ничего сложного, если с сиквелом не один год общаетесь. Искать все ссылки в базе по гуиду с помощью сиквела теперь стало гораздо проще, в отличии от семерки, т.к. там еще в разных таблицах фигурировал в индентификаторе тип и вид объкта.
ПыСы: искал немного другое, наткнулся на эту старую заметку =)
(1)
может это поможет, можно установить для нового элемента:
Валюта = Справочники.Валюты.СоздатьЭлемент();
СтрокаGUID = «b8bea012-3da9-4d01-8855-48811771af7a»;
УникальныйИдентификатор = Новый УникальныйИдентификатор(СтрокаGUID);
Ссылка = Справочники.Валюты.ПолучитьСсылку(УникальныйИдентификатор);
Валюта.УстановитьСсылкуНового(Ссылка);
Валюта.Наименование = «новый рубль»;
Валюта.Записать();
вообще «поискдулей» разве не сработает? заменит одну валюту на другую без перепроведения и удалит лишнюю
Источник