При разработке мобильных приложений на платформе 1С:Предприятие разработчики периодически сталкиваются с ситуацией, когда при попытке удалить запись из независимого регистра сведений мобильное приложение аварийно завершает свою работу (вылетает). При этом в конфигурации могут отсутствовать какие-либо подписки на события или обработчики событий в модуле набора записей. Разберем причины такого поведения и способы надежного устранения ошибки.
Проанализируем основные факторы, вызывающие сбой при работе с регистрами сведений в мобильной среде:
МенеджерЗаписи происходило некорректное освобождение ресурсов или конфликт блокировок (database locked), что приводило к критическому сбою нативного процесса (SIGSEGV).Пока Выборка.Следующий() Цикл) через менеджер записи, мобильная СУБД блокирует файл базы данных из-за одновременной попытки чтения и модификации.Первое и главное действие при возникновении необъяснимых падений нативных методов — проверка и обновление используемой мобильной платформы. Релизы мобильной платформы выпускаются с регулярными исправлениями системных ошибок работы со встроенной СУБД SQLite. Если используется проблемный релиз (например, 8.3.10.51), необходимо пересобрать приложение на стабильной или тестовой сборке с исправлением (например, 8.3.10.77, 8.3.10.80 или более актуальных версиях ветки 8.3).
Посмотрим на наиболее устойчивый и рекомендованный способ программного удаления записей независимого регистра сведений на мобильной платформе. Вместо использования РегистрСведенийМенеджерЗаписи или интерактивных команд списка надежнее использовать запись пустого объекта РегистрСведенийНаборЗаписей с установленным отбором по ключевым измерениям.
Рассмотрим пример кода для удаления записи на сервере:
&НаСервере
Процедура УдалитьЗаписьРегистраНаСервере(ЗначениеКлюча1, ЗначениеКлюча2)
НачатьТранзакцию();
Попытка
Набор = РегистрыСведений.МойНезависимыйРегистр.СоздатьНаборЗаписей();
// Устанавливаем отборы по всем ключевым измерениям
Набор.Отбор.Измерение1.Установить(ЗначениеКлюча1);
Набор.Отбор.Измерение2.Установить(ЗначениеКлюча2);
// Запись пустого набора с замещением удаляет соответствующие записи
Набор.Записать(Истина);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Такой подход гарантирует корректную транзакционную изоляцию и минимизирует вероятность блокировок на уровне встроенной базы данных SQLite.
Если удаление инициируется пользователем через стандартную командную панель динамического списка или контекстное меню, разберем по шагам, как перехватить операцию и избежать сбоя отрисовки интерфейса:
ПередУдалением, установив Отказ = Истина.РегистрСведенийНаборЗаписей.ОповеститьОбИзменении() или команды обновления элементов формы.Пример реализации перехвата удаления в модуле формы:
&НаКлиенте
Процедура СписокПередУдалением(Элемент, Отказ)
Отказ = Истина; // Отменяем стандартное платформенное удаление
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Выполняем безопасное удаление на сервере
УдалитьЗаписьРегистраНаСервере(ТекущиеДанные.Измерение1, ТекущиеДанные.Измерение2);
// Оповещаем интерфейс об изменении данных
ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.МойНезависимыйРегистр"));
Элементы.Список.Обновить();
КонецПроцедуры
Когда требуется очистить или удалить сразу несколько записей по условию, частой ошибкой является чтение через Выборка = РегистрыСведений.МойРегистр.Выбрать() с последующим вызовом менеджера записи прямо внутри цикла. На мобильной платформе это гарантированно приводит к конфликтам с файлом базы данных.
Разберем правильный алгоритм массового удаления:
ТаблицаЗначений).
&НаСервере
Процедура УдалитьУстаревшиеЗаписиНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Регистр.Измерение1 КАК Измерение1,
| Регистр.Измерение2 КАК Измерение2
|ИЗ
| РегистрСведений.МойНезависимыйРегистр КАК Регистр
|ГДЕ
| Регистр.ФлагУдаления = ИСТИНА";
ТаблицаДляУдаления = Запрос.Выполнить().Выгрузить();
НачатьТранзакцию();
Попытка
Для Каждого Стр Из ТаблицаДляУдаления Цикл
Набор = РегистрыСведений.МойНезависимыйРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Измерение1.Установить(Стр.Измерение1);
Набор.Отбор.Измерение2.Установить(Стр.Измерение2);
Набор.Записать(Истина);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Для предотвращения нестабильной работы локальной базы мобильного приложения обратим внимание на структуру самого регистра:
Строка фиксированной длины, Число, Дата, Булево) либо ссылки на конкретные справочники.