Zebra TC27 и Meferi в мобильном приложении 1С на Android 13+: баг в компоненте сканера и его решение (не работает ТСД)

17.06.26

Интеграция - Терминал сбора данных

Подключили Zebra TC27 на Android 14 к мобильному приложению 1С, собранному через Сборщик мобильных приложений. DataWedge отправляет, Подключить() говорит Да, а штрихкод в форму не приходит. Meferi на том же коде работает. Разбираем причину через ADB, находим баг в компоненте Драйвер1СУстройстваВводаNative и фиксим одним байтом.

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
Драйвер1СУстройстваВводаNative
.zip 13,85Mb
0 3 400 руб. Купить
Архив включает: Конфигурация пример реализации получения настроек и apk файлы для ТСД
.zip 229,10Mb
0 4 200 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Вы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

Предыстория: выбор железа

С введением обязательной маркировки товаров на складе возникла реальная потребность в терминалах сбора данных — сканировать коды маркировки вручную через обычный сканер при приёмке и отгрузке стало неудобно и медленно. Встал вопрос выбора устройств и интеграции с мобильным приложением 1С.

При выборе ТСД выбор пал на Zebra TC27. До Android 13 никаких проблем с интеграцией в 1С — всё заводилось без лишних усилий, сканеры у Zebra топовые, достаточно оперативной памяти, хороший экран и отличная камера. Камера для нас принципиальна: планируем использовать её для фиксации товара на входном контроле и при выпуске продукции со склада.

В качестве резервных устройств взяли Meferi с аналогичным набором характеристик — камера там заметно уступает Zebra, но как резервные ТСД они закрывают потребность.

Всё было хорошо, пока Zebra не пришли с Android 14.

 

Компонента и сборка приложения

Для интеграции сканера с мобильным приложением 1С используется официальная внешняя компонента — Драйвер1СУстройстваВводаNative, поставляемая в составе Библиотеки Подключаемого Оборудования (БПО или БПОМП). Она регистрирует системный BroadcastReceiver, слушает broadcast-интенты от сканирующего приложения и передаёт данные в 1С через механизм внешних событий.

Мобильное приложение собирается через Сборщик мобильных приложений 1С. Компонента добавляется в конфигурацию как общий макет (Драйвер1СУстройстваВводаNative) с типом «Внешняя компонента» и при сборке APK включается в пакет приложения. Важно: тип макета должен быть именно «Внешняя компонента», а не «Двоичные данные» — иначе при вызове УстановитьВнешнююКомпоненту() платформа выдаст ошибку типа макета.

Перед сборкой необходимо настроить свойства конфигурации в конфигураторе:

  • Версия (раздел «Разработка») — указать версию приложения, например 1.1.1. Без заполненной версии Сборщик не соберёт APK.
  • Назначения использования — поставить галку «Приложение для мобильной платформы». Без этого конфигурация не будет распознана как мобильное приложение.
  • Используемая функциональность — включить «Воспроизведение аудио и вибрация» (раздел «Мультимедиа»). Требуется для звуковой индикации при сканировании.

Версия компоненты из состава БПО/БПОМП должна соответствовать минимальной поддерживаемой версии мобильной платформы — при несовпадении Подключить() вернёт Ложь. Версия совпадает, компонента подключается, Подключить() возвращает Да — но на Zebra TC27 с Android 14 штрихкод в приложение всё равно не приходит. О причине — ниже.

Альтернативный вариант развёртывания — публикация мобильного приложения на веб-сервере без сборки APK. В этом случае пользователи подключаются через стандартный мобильный клиент 1С. При таком подходе необходимо настроить IIS: добавить MIME-типы для расширений .so, .apk, .dylib с типом application/octet-stream — иначе компонента не загрузится. Подробнее о публикации мобильных приложений читайте в документации 1С.

 

Реализация хранения настроек и подключения

Как именно хранить настройки сканера и организовать подключение компоненты — полностью на вашей совести. Можно использовать константы, регистры сведений, готовые рабочие места из БПО и так далее.

Наша база написана без БСП, БПО и БПОМП, поэтому готовых рабочих мест и механизмов оборудования в ней нет. Мы пошли простым путём — свой регистр сведений и минимальный код. Ниже показан именно этот вариант как пример реализации.

Для корректного подключения компоненты ей необходимо передать набор параметров — режим работы, action, extra, имя события и ряд других. Чтобы не прописывать их жёстко в коде и иметь возможность менять без пересборки приложения, параметры нужно где-то хранить. Сама компонента умеет отдавать список своих параметров с описанием и допустимыми значениями — оттуда и начнём.

 

Форма НастройкиСканера

Для удобного управления параметрами создаём общую форму НастройкиСканера. На форме создаём три команды:

  • ОпределитьПараметрыСканера — кнопка «Получить сохранённые настройки». Подключает компоненту, вызывает ПолучитьПараметры(), получает XML с описанием всех параметров и динамически строит поля формы. В первый раз поля заполняются значениями по умолчанию из XML — при последующих запусках подставляются уже сохранённые значения из регистра сведений.
  • Сохранить — записывает все параметры из полей формы в регистр сведений. При следующем подключении сканера они передаются в компоненту автоматически.
  • КнопкаЗакрыть — закрывает форму.

Также в конфигураторе необходимо привязать событие формы ПриОткрытии к процедуре ПриОткрытии — это делается в свойствах формы на вкладке «События». Если хотите чтобы параметры подтягивались автоматически при открытии формы, вызовите ОпределитьПараметрыСканера из обработчика ПриОткрытии.

// Общая форма: НастройкиСканера
// Форма динамически строит интерфейс на основе XML-описания параметров компоненты.
// Все динамические реквизиты создаются с префиксом Скн_ — это позволяет
// легко их находить, обновлять и удалять при перестройке формы.

#Область ОбработчикиСобытийФормы

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Здесь можно загрузить дополнительные настройки вашей конфигурации,
    // например параметры подключения к серверу 1С
КонецПроцедуры

#КонецОбласти

#Область ОбработчикиКомандФормы

&НаКлиенте
Процедура Сохранить(Команда)
    // Сохраняем все параметры сканера в регистр сведений
    СохранитьПараметрыСканераНаСервере();
    ПоказатьПредупреждение(, "Настройки сохранены.");
КонецПроцедуры

&НаКлиенте
Процедура КнопкаЗакрыть(Команда)
    Закрыть();
КонецПроцедуры

// Кнопка "Получить сохранённые настройки":
// — в первый раз подключает компоненту, получает XML с параметрами
//   и строит форму со значениями по умолчанию из XML
// — при последующих нажатиях подставляет уже сохранённые значения из регистра
&НаКлиенте
Процедура ОпределитьПараметрыСканера(Команда)

    УстановитьВнешнююКомпоненту("ОбщийМакет.Драйвер1СУстройстваВводаNative");

    Если ПодключитьВнешнююКомпоненту(
            "ОбщийМакет.Драйвер1СУстройстваВводаNative",
            "AddIn",
            ТипВнешнейКомпоненты.Native) Тогда

        InputDevice = Новый("AddIn.AddIn.InputDevice");

        // Получаем XML с описанием всех параметров компоненты
        ОписаниеИнтерфейса = "";
        InputDevice.ПолучитьПараметры(ОписаниеИнтерфейса);

        // Загружаем ранее сохранённые значения из регистра сведений
        // (при первом запуске регистр пуст — будут использованы значения по умолчанию)
        СохраненныеЗначения = ПолучитьСохраненныеПараметрыНаСервере();

        // Строим интерфейс на сервере — динамически создаём реквизиты и поля формы
        ПостроитьИнтерфейсСканераНаСервере(ОписаниеИнтерфейса, СохраненныеЗначения);

    Иначе
        Сообщить("Не удалось подключить компоненту сканера.");
    КонецЕсли;

КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

&НаСервере
Функция ПолучитьСохраненныеПараметрыНаСервере()
    // Возвращает соответствие ИмяПараметра -> Значение из регистра сведений
    Возврат РаботаСДрайверомОборудования.ПолучитьПараметрыСканера();
КонецФункции

&НаСервере
Процедура СохранитьПараметрыСканераНаСервере()
    // Перебираем все реквизиты формы с префиксом Скн_ —
    // это динамически созданные параметры сканера.
    // Вырезаем префикс (4 символа) чтобы получить оригинальное имя параметра
    // и сохраняем значение в регистр сведений.
    РеквизитыФормы = ПолучитьРеквизиты();
    Для Каждого Реквизит Из РеквизитыФормы Цикл
        Если Лев(Реквизит.Имя, 4) = "Скн_" Тогда
            ИмяПараметра = Прав(Реквизит.Имя, СтрДлина(Реквизит.Имя) - 4);
            РаботаСДрайверомОборудования.СохранитьПараметрСканера(
                ИмяПараметра,
                Строка(ЭтотОбъект[Реквизит.Имя]));
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаСервере
Процедура ПостроитьИнтерфейсСканераНаСервере(ОписаниеИнтерфейса, СохраненныеЗначения)

    // Шаг 1: парсим XML и получаем массив структур с описанием параметров
    МассивПараметров = РазобратьXMLПараметров(ОписаниеИнтерфейса);

    // Шаг 2: удаляем старые динамические реквизиты и элементы (если форма уже строилась)
    // Сначала удаляем элементы формы (визуальные поля), потом реквизиты —
    // важно делать именно в таком порядке, иначе платформа выбросит ошибку.
    // Реквизиты удаляем одним вызовом ИзменитьРеквизиты после цикла,
    // потому что нельзя вызывать его внутри цикла по ПолучитьРеквизиты.
    УдаляемыеРеквизиты = Новый Массив;
    Для Каждого Рекв Из ПолучитьРеквизиты() Цикл
        Если Лев(Рекв.Имя, 4) = "Скн_" Тогда
            УдаляемыеРеквизиты.Добавить(Рекв.Имя);
            Если Элементы.Найти(Рекв.Имя) <> Неопределено Тогда
                Элементы.Удалить(Элементы[Рекв.Имя]);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Если УдаляемыеРеквизиты.Количество() > 0 Тогда
        ИзменитьРеквизиты(, УдаляемыеРеквизиты);
    КонецЕсли;

    // Шаг 3: убеждаемся что группа для полей сканера существует
    Если Элементы.Найти("ГруппаСканер") = Неопределено Тогда
        ГруппаСканер = Элементы.Добавить("ГруппаСканер", Тип("ГруппаФормы"));
        ГруппаСканер.Вид         = ВидГруппыФормы.ОбычнаяГруппа;
        ГруппаСканер.Заголовок   = "Сканер";
        ГруппаСканер.РастягиватьПоГоризонтали = Истина;
    Иначе
        ГруппаСканер = Элементы["ГруппаСканер"];
    КонецЕсли;

    // Шаг 4: добавляем все реквизиты одним вызовом ИзменитьРеквизиты —
    // это быстрее и безопаснее чем добавлять по одному
    ДобавляемыеРеквизиты = Новый Массив;
    Для Каждого Параметр Из МассивПараметров Цикл
        Если Параметр.Тип = "NUMBER" Тогда
            Рекв = Новый РеквизитФормы(Параметр.ИмяРеквизита, Новый ОписаниеТипов("Число"), , Параметр.Заголовок, Истина);
        ИначеЕсли Параметр.Тип = "BOOLEAN" Тогда
            Рекв = Новый РеквизитФормы(Параметр.ИмяРеквизита, Новый ОписаниеТипов("Булево"), , Параметр.Заголовок, Истина);
        Иначе
            Рекв = Новый РеквизитФормы(Параметр.ИмяРеквизита, Новый ОписаниеТипов("Строка"), , Параметр.Заголовок, Истина);
        КонецЕсли;
        ДобавляемыеРеквизиты.Добавить(Рекв);
    КонецЦикла;
    Если ДобавляемыеРеквизиты.Количество() > 0 Тогда
        ИзменитьРеквизиты(ДобавляемыеРеквизиты);
    КонецЕсли;

    // Шаг 5: создаём элементы формы и заполняем значениями
    БазоваяГруппа = Неопределено;
    Для Каждого Параметр Из МассивПараметров Цикл

        // Создаём контейнер для полей при первой итерации
        Если БазоваяГруппа = Неопределено Тогда
            БазоваяГруппа = Элементы.Добавить("БазоваяГруппа0", Тип("ГруппаФормы"), ГруппаСканер);
            БазоваяГруппа.Вид         = ВидГруппыФормы.ОбычнаяГруппа;
            БазоваяГруппа.РастягиватьПоГоризонтали = Истина;
            БазоваяГруппа.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная;
        КонецЕсли;

        НовЭлемент = Элементы.Добавить(Параметр.ИмяРеквизита, Тип("ПолеФормы"), БазоваяГруппа);

        // Для булевых параметров — флажок, для остальных — поле ввода
        Если Параметр.Тип = "BOOLEAN" Тогда
            НовЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
        Иначе
            НовЭлемент.Вид                      = ВидПоляФормы.ПолеВвода;
            НовЭлемент.АвтоМаксимальнаяШирина   = Ложь;
            НовЭлемент.РастягиватьПоГоризонтали = Истина;
            НовЭлемент.Формат                   = Параметр.СтрокаФорматирования;
            НовЭлемент.ФорматРедактирования     = Параметр.СтрокаФорматирования;

            // Если у параметра есть список допустимых значений — включаем выбор из списка
            Если Параметр.СписокВыбора.Количество() > 0 Тогда
                НовЭлемент.РежимВыбораИзСписка  = Истина;
                НовЭлемент.ВысотаСпискаВыбора   = 10;
                НовЭлемент.РедактированиеТекста = Ложь;
                Для Каждого ЭлСписка Из Параметр.СписокВыбора Цикл
                    НовЭлемент.СписокВыбора.Добавить(ЭлСписка.Значение, ЭлСписка.Представление);
                КонецЦикла;
            КонецЕсли;
        КонецЕсли;

        НовЭлемент.ПутьКДанным   = Параметр.ИмяРеквизита;
        НовЭлемент.Подсказка      = Параметр.Описание;
        НовЭлемент.ТолькоПросмотр = Ложь;

        // Устанавливаем значение: из регистра если уже сохранено, иначе по умолчанию из XML
        ЗначениеДляУстановки = Параметр.ЗначениеПоУмолчанию;
        Если СохраненныеЗначения.Получить(Параметр.ОригинальноеИмя) <> Неопределено Тогда
            ЗначениеДляУстановки = СохраненныеЗначения[Параметр.ОригинальноеИмя];
        КонецЕсли;

        Если НЕ ПустаяСтрока(ЗначениеДляУстановки) Тогда
            Если Параметр.Тип = "BOOLEAN" Тогда
                ЭтотОбъект[Параметр.ИмяРеквизита] =
                    ВРег(ЗначениеДляУстановки) = "TRUE"
                    Или ВРег(ЗначениеДляУстановки) = "ИСТИНА";
            ИначеЕсли Параметр.Тип = "NUMBER" Тогда
                ЭтотОбъект[Параметр.ИмяРеквизита] = Число(ЗначениеДляУстановки);
            Иначе
                ЭтотОбъект[Параметр.ИмяРеквизита] = ЗначениеДляУстановки;
            КонецЕсли;
        КонецЕсли;

    КонецЦикла;

КонецПроцедуры

&НаСервере
Функция РазобратьXMLПараметров(ОписаниеИнтерфейса)
    // Парсим XML который возвращает компонента через ПолучитьПараметры().
    // Структура XML: корневой элемент Settings, внутри элементы Parameter,
    // у каждого может быть вложенный ChoiceList со списком допустимых значений.
    // Возвращаем массив структур — по одной на каждый редактируемый параметр.

    МассивПараметров = Новый Массив;
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(ОписаниеИнтерфейса);
    ЧтениеXML.ПерейтиКСодержимому();

    ТекущийПараметр = Неопределено;

    Если ЧтениеXML.Имя = "Settings"
        И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

        Пока ЧтениеXML.Прочитать() Цикл

            Если ЧтениеXML.Имя = "Parameter"
                И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

                // Параметры только для чтения пропускаем — они нам не нужны.
                // Сбрасываем ТекущийПараметр чтобы ChoiceList readonly параметра
                // случайно не добавился к предыдущему редактируемому параметру.
                Если ВРег(ЧтениеXML.ЗначениеАтрибута("ReadOnly")) = "TRUE" Тогда
                    ТекущийПараметр = Неопределено;
                    Продолжить;
                КонецЕсли;

                ОригинальноеИмя = ЧтениеXML.ЗначениеАтрибута("Name");
                // Префикс Скн_ — соглашение об именовании динамических реквизитов сканера.
                // По нему легко найти, обновить или удалить все поля формы сканера
                // не затрагивая статические реквизиты конфигурации.
                ИмяРеквизита = "Скн_" + ОригинальноеИмя;

                ТекущийПараметр = Новый Структура;
                ТекущийПараметр.Вставить("ОригинальноеИмя",      ОригинальноеИмя);
                ТекущийПараметр.Вставить("ИмяРеквизита",         ИмяРеквизита);
                ТекущийПараметр.Вставить("Заголовок",            ЧтениеXML.ЗначениеАтрибута("Caption"));
                ТекущийПараметр.Вставить("Тип",                  ВРег(?(НЕ ПустаяСтрока(ЧтениеXML.ЗначениеАтрибута("TypeValue")), ЧтениеXML.ЗначениеАтрибута("TypeValue"), "STRING")));
                ТекущийПараметр.Вставить("ЗначениеПоУмолчанию",  ЧтениеXML.ЗначениеАтрибута("DefaultValue"));
                ТекущийПараметр.Вставить("Описание",             ЧтениеXML.ЗначениеАтрибута("Description"));
                ТекущийПараметр.Вставить("СтрокаФорматирования", ЧтениеXML.ЗначениеАтрибута("FieldFormat"));
                ТекущийПараметр.Вставить("СписокВыбора",         Новый СписокЗначений);

                МассивПараметров.Добавить(ТекущийПараметр);

            ИначеЕсли ЧтениеXML.Имя = "ChoiceList"
                И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента
                И ТекущийПараметр <> Неопределено Тогда

                // Читаем список допустимых значений параметра
                Пока ЧтениеXML.Прочитать() Цикл
                    Если ЧтениеXML.Имя = "ChoiceList"
                        И ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
                        Прервать;
                    КонецЕсли;
                    Если ЧтениеXML.Имя = "Item"
                        И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
                        ЗначениеАтрибута = ЧтениеXML.ЗначениеАтрибута("Value");
                        Если ЧтениеXML.Прочитать() Тогда
                            ПредставлениеАтрибута = ЧтениеXML.Значение;
                        КонецЕсли;
                        // Если Value пустой — используем текст элемента как значение
                        Если ПустаяСтрока(ЗначениеАтрибута) Тогда
                            ЗначениеАтрибута = ПредставлениеАтрибута;
                        КонецЕсли;
                        ТекущийПараметр.СписокВыбора.Добавить(ЗначениеАтрибута, ПредставлениеАтрибута);
                    КонецЕсли;
                КонецЦикла;

            КонецЕсли;
        КонецЦикла;
    КонецЕсли;

    ЧтениеXML.Закрыть();
    Возврат МассивПараметров;

КонецФункции

#КонецОбласти

 

Сохранение параметров

После того как параметры получены и скорректированы под устройство, сохраняем их в регистр сведений. Для этого в серверном общем модуле РаботаСДрайверомОборудования создаём процедуру сохранения. Общий модуль должен быть серверным и не глобальным — это важно, так как он вызывается с сервера из формы настроек.

// Серверный общий модуль РаботаСДрайверомОборудования

Процедура СохранитьПараметрСканера(Параметр, Значение) Экспорт

    НаборЗаписей = РегистрыСведений.НастройкиСканера.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Параметр.Установить(Параметр);

    Запись          = НаборЗаписей.Добавить();
    Запись.Параметр = Параметр;
    Запись.Значение = Значение;

    НаборЗаписей.Записать();

КонецПроцедуры

 

Получение параметров из регистра сведений

Для хранения параметров создаём в конфигурации регистр сведений НастройкиСканера. Состав реквизитов:

Измерения:

  • Параметр — Строка (100). Имя параметра драйвера.

Ресурсы:

  • Значение — Строка (256). Значение параметра.

Там же, в модуле РаботаСДрайверомОборудования, размещаем функцию получения параметров из регистра — она используется при подключении сканера в форме документа:

Функция ПолучитьПараметрыСканера() Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |   НастройкиСканера.Параметр,
    |   НастройкиСканера.Значение
    |ИЗ
    |   РегистрСведений.НастройкиСканера КАК НастройкиСканера";

    Результат = Новый Соответствие;
    Выборка   = Запрос.Выполнить().Выбрать();

    Пока Выборка.Следующий() Цикл
        Результат.Вставить(Выборка.Параметр, Выборка.Значение);
    КонецЦикла;

    Возврат Результат;

КонецФункции

 

Код модуля формы

Настройки сохранены — теперь создаём форму документа для работы со сканером. В примере это простая форма с реквизитами Дата и Номер, которая при сканировании выводит код маркировки в сообщение. Весь остальной функционал — поиск номенклатуры, запись в документ, проверки — реализуете самостоятельно под свою задачу.

Параметры читаются из регистра сведений и передаются в компоненту через цикл — никакой жёсткой прошивки значений в коде нет.

В конфигураторе в свойствах формы документа необходимо привязать следующие события и обработчики:

  • ПриОткрытии → процедура ПриОткрытии — подключает сканер при открытии формы.
  • ПриЗакрытии → процедура ПриЗакрытии — отключает сканер при закрытии формы.
  • ВнешнееСобытие → процедура ВнешнееСобытие — стандартное событие мобильной платформы, через которое компонента передаёт данные сканера в форму. Привязывается там же на вкладке «События» формы.
// Объявить в самом начале модуля формы до всех процедур
&НаКлиенте
Перем Компонента, КомпонентаПодключена;

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьСканер();
КонецПроцедуры

&НаКлиенте
Процедура ПриЗакрытии(СтандартнаяОбработка)
    ОтключитьСканер();
КонецПроцедуры

// Получает данные от сканера
&НаКлиенте
Процедура ВнешнееСобытие(Источник, Событие, Данные)

    Если НЕ КомпонентаПодключена Тогда
        Возврат;
    КонецЕсли;

    Если СтрНайти(Источник, "InputDevice") = 0 Тогда
        Возврат;
    КонецЕсли;

    Штрихкод = СокрЛП(Данные);

    Если ПустаяСтрока(Штрихкод) Тогда
        Возврат;
    КонецЕсли;

    Сообщить("Код маркировки: " + Штрихкод);

КонецПроцедуры

// Читает параметры из регистра и подключает компоненту
&НаКлиенте
Процедура ПодключитьСканер()

    ПараметрыСканера = ПолучитьПараметрыСканераНаСервере();

    УстановитьВнешнююКомпоненту("ОбщийМакет.Драйвер1СУстройстваВводаNative");

    Если ПодключитьВнешнююКомпоненту(
            "ОбщийМакет.Драйвер1СУстройстваВводаNative",
            "AddIn",
            ТипВнешнейКомпоненты.Native) Тогда

        Компонента = Новый("AddIn.AddIn.InputDevice");

        Для Каждого КлючЗначение Из ПараметрыСканера Цикл
            Компонента.УстановитьПараметр(КлючЗначение.Ключ, КлючЗначение.Значение);
        КонецЦикла;

        Компонента.Подключить("");
        КомпонентаПодключена = Истина;

    Иначе
        КомпонентаПодключена = Ложь;
        Сообщить("Не удалось подключить компоненту сканера.");
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ОтключитьСканер()

    Если Компонента <> Неопределено Тогда
        Компонента.Отключить("");
        Компонента = Неопределено;
    КонецЕсли;

    КомпонентаПодключена = Ложь;

КонецПроцедуры

&НаСервере
Функция ПолучитьПараметрыСканераНаСервере()
    Возврат РаботаСДрайверомОборудования.ПолучитьПараметрыСканера();
КонецФункции

 

Звуковая индикация (только в собранном APK)

Мы добавили звуковую индикацию в своё приложение — воспроизводим звук успеха когда штрихкод обработан успешно, и звук ошибки когда номенклатура не найдена или что-то пошло не так. Это удобно на складе: сотруднику не нужно смотреть на экран после каждого сканирования. Делимся на случай если кому-то пригодится.

Аудиофайлы берёте любые подходящие с интернета. При подключении столкнулись с неочевидной проблемой: Сборщик мобильных приложений молча не включал звуковые файлы в сборку. Оказалось, причина в структуре ZIP-архива — он требует папки для всех трёх платформ, даже пустые. Как только создали папки iOS/ и Windows/ — файлы начали включаться в сборку. Структура архива должна быть такой:

Android/
    beep_success.mp3
    beep_error.mp3
iOS/
Windows/

Папки iOS/ и Windows/ пустые — они нужны только чтобы Сборщик корректно обработал архив. Готовый ZIP добавьте в Сборщик в раздел «Аудиоресурсы». Также в свойствах конфигурации в разделе «Используемая функциональность» необходимо включить «Воспроизведение аудио и вибрация» — без этого звук работать не будет.

&НаКлиенте
Процедура ЗвукУспеха()
#Если МобильноеПриложениеКлиент Тогда
    СредстваМультимедиа.ВоспроизвестиЗвуковоеОповещение("beep_success", Ложь);
#КонецЕсли
КонецПроцедуры

&НаКлиенте
Процедура ЗвукОшибки()
#Если МобильноеПриложениеКлиент Тогда
    СредстваМультимедиа.ВоспроизвестиЗвуковоеОповещение("beep_error", Истина);
#КонецЕсли
КонецПроцедуры

Второй параметр Истина включает вибрацию. Для ошибки — с вибрацией, для успеха — без.

 

Подсистема и команды

Чтобы наши формы были доступны в интерфейсе приложения, необходимо создать подсистему и общие команды для их открытия — без этого формы просто не появятся в меню.

 

Подсистема ОсновнаяПодсистема

В конфигураторе создаём подсистему ОсновнаяПодсистема. В неё включаем обе общие команды — они будут отображаться в навигационном меню мобильного приложения.

 

Общие команды

Создаём две общие команды и включаем их в подсистему ОсновнаяПодсистема.

ОткрытьНастройкиСканера — открывает форму настройки параметров сканера:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    ОткрытьФорму("ОбщаяФорма.НастройкиСканера");
КонецПроцедуры

ОткрытьФормуТестирования — открывает форму для тестирования сканирования:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    ОткрытьФорму("ОбщаяФорма.ФормаТестирования");
КонецПроцедуры

После создания команд в свойствах каждой команды необходимо:

  • На вкладке «Подсистемы» поставить галку напротив ОсновнаяПодсистема.
  • В поле «Группа» указать группу команды — например Панель навигации.Важное. Без этого конфигуратор выдаст ошибку «Не указана группа, в которую входит команда по умолчанию» и не даст собрать приложение.

 

Настройка ТСД и параметры компоненты

 

Zebra TC27 — настройка DataWedge

DataWedge предустановлен на всех устройствах Zebra. Создаём новый профиль:

  • При создании профиля сразу указываем имя и в разделе Associated Apps добавляем пакет нашего приложения — DataWedge будет активировать профиль автоматически при запуске именно этого приложения.
  • Все остальные профили DataWedge желательно отключить, чтобы они не конкурировали с нашим.
  • Barcode Input — включить.
  • Keystroke Output — отключить.
  • Intent Output — включить со следующими настройками:
  •  
Параметр DataWedge Значение
Intent action произвольный (совпадает с параметром Action в регистре 1С)
Intent category оставить пустым
Intent delivery Broadcast intent
Component information не заполнять
Receiver foreground flag 1

 

Параметры для сохранения в регистр сведений 1С. Три выделенных жирным — обязательные, именно они определяют режим работы и маршрут доставки данных. Остальные параметры можно оставить со значениями по умолчанию — всё заработает:

 

Параметр Значение Примечание
Устройство BROADCAST Обязательно
Пользовательский intent action com.zebra.scan Обязательно. Произвольный, совпадает с DataWedge
Пользовательский intent extra com.symbol.datawedge.data_string Стандартный ключ согласно документации DataWedge и данным форумов

 

Meferi — настройка meWedge

При создании профиля в meWedge сразу указывается имя профиля и приложение, для которого он используется — это и есть привязка к нашему приложению. Отдельного шага добавления не требуется.

Настройки широковещательного вывода:

 

Параметр meWedge Значение
Broadcast Action произвольный action (совпадает с параметром Action в регистре 1С)
Barcode label data — имя Extra, в которое meWedge кладёт данные штрихкода

Параметры для регистра сведений 1С — аналогичны Zebra, отличаются только два:

Параметр Значение Примечание
Пользовательский intent action com.meferi.scan Обязательно. Произвольный, совпадает с meWedge
Пользовательский intent extra data Обязательно. Barcode label из meWedge

 

Собираем, устанавливаем — и начинаются танцы с бубнами

Приложение собрано через Сборщик мобильных приложений 1С, APK установлен на оба устройства. Подключить() возвращает Да на обоих.

Включаем Meferi — сканируем, данные прилетают в форму. Работает.

Включаем Zebra TC27 с Android 14 — сканируем, тишина. Никаких ошибок, Подключить() говорит Да, DataWedge в своих логах подтверждает отправку — а в 1С пусто.

 

Идём в ADB разбираться

Для диагностики понадобится Android Debug Bridge (ADB) — инструмент из состава Android SDK. Подробности по установке легко найти в интернете. Подключаем Zebra к компьютеру и смотрим logcat во время сканирования:

adb logcat | grep -i datawedge

DataWedge исправно шлёт broadcast при каждом сканировании — в логе видны записи от IntentPlugin с нашим action, адресованные пакету приложения. Данные просто не доходят до получателя.

Делаем дамп очереди broadcast после сканирования:

adb shell dumpsys activity broadcasts | grep -i "denial" > bc.txt

В bc.txt находим:

skipped by policy at enqueue: Exported Denial — receiver not specifying RECEIVER_EXPORTED

Android молча выбрасывает broadcast, не донося до получателя. Ни исключений, ни ошибок — именно поэтому так сложно диагностировать.

 

Почему на Meferi работает, а на Zebra нет

Начиная с Android 13 (API 33) Google ввёл правило: если обычное приложение отправляет broadcast другому, получатель обязан явно объявить флаг RECEIVER_EXPORTED = 2. Без него Android блокирует доставку тихо, без ошибок.

DataWedge на Zebra — обычное приложение (uid 10054). Правило применяется в полный рост.

Сканерный сервис на Meferi встроен в прошивку и работает как системный процесс (uid 1000). Для системных отправителей это ограничение не действует — вот почему Meferi работает без проблем даже на Android 15.

Лезем в код компоненты. Распаковываем ZIP, внутри APK com_1c_ScanOPOS.apk, внутри него classes.dex. В методе PBroadcastReceaver.open (опечатка в оригинале — именно так) находим:

const/4 v2, 0x4   ; передаётся как флаг в registerReceiver

0x4 — это Context.RECEIVER_NOT_EXPORTED. Компонента сама себе запрещает принимать broadcast от внешних приложений. Это баг в компоненте.

Эта проблема не специфична для Zebra. Любой ТСД на Android 13 и выше, где сканер доставляет данные через broadcast от обычного приложения (не системного процесса), столкнётся с тем же.

 

Решение: патч одного байта

Меняем 0x4 на 0x2 (RECEIVER_EXPORTED) в classes.dex и пересчитываем контрольные суммы DEX-заголовка (Adler32 + SHA1) — без этого Android откажется загружать файл.

APK компоненты не нужно подписывать: мобильная платформа 1С загружает его как runtime-компоненту из директории ExtCompT, а не как самостоятельное приложение.

Готовый патченный архив Драйвер1СУстройстваВводаNative_patched.zip приложен к статье. Заменить им макет в конфигурации, пересобрать APK через Сборщик, переустановить на устройство — рекомендуется полное удаление, платформа кэширует компоненту.

После замены макета, пересборки и переустановки приложения DataWedge начинает исправно доставлять данные в 1С — штрихкод приходит в форму при каждом сканировании. Проблема решена.

 

Как пропатчить самостоятельно

Потребуются Python 3 и утилиты baksmali/smali — информация об установке легко находится в интернете, проект доступен на github.com/JesusFreke/smali.

# 1. Распаковать компоненту
unzip Драйвер1СУстройстваВводаNative.zip com_1c_ScanOPOS.apk
unzip com_1c_ScanOPOS.apk classes.dex

# 2. Дизассемблировать
java -jar baksmali.jar disassemble classes.dex -o smali_out/

В папке smali_out найти файл PBroadcastReceaver.smali, найти вызов registerReceiver с 4 аргументами и рядом стоящую инструкцию const/4 vX, 0x4 — изменить на const/4 vX, 0x2.

# 3. Собрать обратно (контрольные суммы пересчитаются автоматически)
java -jar smali.jar assemble smali_out/ -o classes_patched.dex
# 4. Обновить APK и ZIP компоненты
cp com_1c_ScanOPOS.apk com_1c_ScanOPOS_patched.apk
zip com_1c_ScanOPOS_patched.apk classes_patched.dex
# Заменить com_1c_ScanOPOS.apk в ZIP компоненты на patched версию

 

Состав вложений

К статье приложены два файла.

Драйвер1СУстройстваВводаNative — патченная компонента. Добавьте её в конфигурацию как общий макет с типом «Внешняя компонента», пересоберите APK через Сборщик мобильных приложений и переустановите на устройство. Рекомендуется полное удаление перед установкой — платформа кэширует компоненту.

Инфостарт.zip — архив с двумя файлами:

  • Конфигурация — пример реализации из статьи. Набросок приложения для сканирования кодов маркировки: общая форма настроек сканера, форма тестирования со сканированием и выводом кода в сообщение. Полноценный функционал обработки кодов маркировки намеренно не реализован — дорабатывайте под свои задачи самостоятельно.
  • Архив с APK — собранное приложение для быстрой проверки на устройстве. Установите на ТСД, настройте DataWedge или meWedge согласно разделу про настройку ТСД и убедитесь что сканирование работает до того как начнёте встраивать код в свою конфигурацию.

 

Чеклист: если всё равно не работает

 

Симптом Причина и решение
Подключить() возвращает Ложь Версия компоненты не совпадает с минимальной поддерживаемой версией платформы.
Да, но событие не приходит Запустить adb shell dumpsys activity broadcasts, найти Exported Denial — нужен патч компоненты.
Exported Denial нет, событие не приходит Проверить посимвольное совпадение Action в регистре 1С и в DataWedge/meWedge.
Работает при открытии, потом перестаёт Перем Компонента объявлен внутри процедуры, а не на уровне модуля формы.
УстановитьВнешнююКомпоненту падает с HTTP 404 В IIS не добавлен MIME-тип: расширение .so, тип application/octet-stream.
DataWedge не активируется при запуске приложения Проверить Associated Apps в профиле DataWedge — пакет приложения должен быть добавлен. Остальные профили DataWedge отключить.

 

Итог

Корень проблемы — один байт в DEX-байткоде компоненты Драйвер1СУстройстваВводаNative. Она регистрировала BroadcastReceiver с флагом RECEIVER_NOT_EXPORTED, что на Android 13+ блокирует приём broadcast от любого обычного приложения-сканера. Meferi работала без патча исключительно потому, что её сканерный сервис системный.

Если ваш ТСД работает на Android 13 или выше и сканер доставляет данные не через системный процесс — патч обязателен.

Патченный ZIP прилагается к статье.

Вступайте в нашу телеграмм-группу Инфостарт

1С мобильное приложение сборщик мобильных приложений Zebra Meferi DataWedge meWedge сканер штрихкодов ТСД Терминал сбора данных Android Android 14 Android 15 мобильная платформа Драйвер1СУстройстваВводаNative BroadcastReceiver RECEIVER_EXPORTED intent broadcast APK внешняя компонента

См. также

Сканер штрих-кода Терминал сбора данных Мобильная разработка Монитор заказов Оптовая торговля Розничная торговля Ценообразование, анализ цен Программист Пользователь 1С:Предприятие 8 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Управленческий учет Платные (руб)

Простой мобильный ТСД (терминал сбора данных) сканер для 1С для смартфонов на iOS и Android, не требующий сложных настроек и установки дополнительных программ. Обмен между Вашей 1С и мобильным приложением осуществляется через облачный сервис и расширение конфигурации. Работает с конфигурациями УТ 11, ERP, КА2, Розница 2, Розница 3, УНФ 1.6, УНФ 3.0. Полнофункциональный демо-доступ для своей конфигурации можно запросить в настройках мобильного приложения - все необходимое придет на почту автоматически. Решение предназначено для считывания штрихкодов, а не для их создания и печати.

3050 руб.

22.04.2019    120845    723    205    

380

Мобильная разработка Сканер штрих-кода Терминал сбора данных 1С:Предприятие 8 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Сбор заказов, инвентаризация, проверка ценников, просмотр полной информации об остатках и ценах со смартфона Онлайн - все это содержит в себе решение 1С "Штрихкод-информер" (штрих-код чекер). Отправка данных со смартфона выполняется либо напрямую в открытую форму документа, отсканировав QR-код, либо в общую корзину учетной системы, не подходя к компьютеру. Кассир или оператор сможет просмотреть список присланных данных и загрузить в любую форму, поддерживающую работу с ТСД. Для работы с мобильным приложением требуется опубликовать HTTP-сервис из поставляемого расширения.

3050 руб.

03.12.2018    70530    239    106    

187

Сканер штрих-кода Терминал сбора данных WEB-интеграция Распознавание документов и образов Программист 1С 8.3 1С:Библиотека стандартных подсистем Бесплатно (free)

Коротко о новых возможностях 1Scan: WebSocket-обмен с Android-устройством, передача штрихкодов, фото, файлов и координат в 1С, а также OCR-распознавание текста камерой.

03.06.2026    458    kild    3    

3

Терминал сбора данных Программист 1С 8.3 Абонемент ($m)

Купили ТСД на Android 13-15, или обновили прошивку сканера, и мобильная платформа 1С перестала принимать аппаратный Enter? Старые внешние компоненты падают с ошибками, а склад стоит? 📦 В этой статье делюсь решением как оживить любые сканеры с помощью легковесного самописного APK. Решение последней надежды, когда штатные методы сдались.

3 стартмани

04.05.2026    650    da_1c    0    

4

Мобильная разработка Терминал сбора данных Программист 1С 8.3 1С:Управление нашей фирмой 3.0 Россия Абонемент ($m)

Возникла необходимость написать мобильное приложение на платформе 1С под терминал сбора данных Cipher RK 26. В этом терминале, само собой, есть встроенный сканер ШК. Мое приложение должно как-то работать с ним. Самый простой вариант - через эмуляцию клавиатуры, но у этого способа есть минусы: служебные символы, типа GS1, могут не приходить, или приходить с искажениями, корректность ввода зависит от фокуса и режима редактирования поля штрихкода, постоянная борьба с экранной клавиатурой, и т.д. И есть другой вариант - ловить так называемые "интенты" (broadcast intent) - события ОС Андроид. А как их ловить в 1С? Оказывается, это умеет делать типовой "драйвер устройств ввода", включенный в БПО 1С. Вот об этом варианте я и хочу рассказать. Также предлагаю скачать тестовую конфигурацию мобильного приложения, демонстрирующую результат. Но это не обязательно, т.к. все настройки опишу далее.

5 стартмани

23.04.2026    795    kirlog    7    

3

Логистика, склад и ТМЦ Терминал сбора данных Программист Пользователь 1С 8.3 1С 8.5 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Абонемент ($m)

Проверка и подбор маркируемой продукции с помощью мобильного терминала сбора данных. Простое и элегантное решение для склада, которое позволяет осуществить сверку маркируемой продукции при поступлении и сделать подбор в документ реализации при отгрузке. Работает посредством бесплатного мобильного клиента (не путать с мобильным приложением) в режиме онлайн с подключением к базе данных напрямую.

1 стартмани

17.04.2026    1160    11    Avkisl    6    

3

Терминал сбора данных Системный администратор Программист 1С 8.3 Абонемент ($m)

Набор мобильных конфигураций с примерами работы со встроенными сканерами ТСД различных производителей.

10 стартмани

07.04.2026    2192    11    aximo    13    

9
Для отправки сообщения требуется регистрация/авторизация