Почему возникает ошибка 'Недостаточно памяти' при изменении размера изображений на мобильной платформе 1С и как ее исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке автономных мобильных приложений на платформе 1С, особенно когда требуется работа с графикой, мы можем столкнуться с неожиданными проблемами, которые не проявляются при разработке на десктопе. Одной из таких распространенных трудностей является ошибка "Недостаточно памяти для загрузки картинки", которая возникает при попытке изменить размер изображения с использованием объекта ОбрабатываемаяКартинка и метода УстановитьРазмер на мобильной платформе.

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


Обработка = Новый ОбрабатываемаяКартинка(ИсходнаяКартинка);
Обработка.УстановитьРазмер(МаксимальнаяШирина, Неопределено); // Здесь возникает ошибка на мобильной платформе
ИтоговаяКартинка = Обработка.ПолучитьКартинку;

На десктопе этот код отрабатывает без проблем, но на мобильной платформе (как в эмуляторе, так и на реальном устройстве под Android) мы получаем ошибку "Недостаточно памяти для загрузки картинки" именно в момент вызова метода УстановитьРазмер.

Выясним причину такого поведения и предложим несколько рабочих решений.

Причина ошибки: декомпрессия изображения в память

Основная причина этой ошибки заключается в том, что объект ОбрабатываемаяКартинка на мобильной платформе, при вызове метода УстановитьРазмер, пытается полностью распаковать исходное изображение в оперативную память устройства. В отличие от настольных систем, мобильные устройства обладают ограниченным объемом оперативной памяти. Несжатое изображение, например, в формате RGBA, может занимать в несколько раз больше места, чем его исходный файл на диске. Например, фотография размером 4000x3000 пикселей в несжатом виде может потребовать около 48 МБ памяти. Если приложение уже потребляет значительную часть доступной памяти или само устройство имеет скромные ресурсы, попытка выделить такой объем памяти может привести к ошибке "Недостаточно памяти".

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

Решение 1: Выполнение обработки на сервере (&НаСервере)

Это наиболее надежный и рекомендуемый способ, если ваше мобильное приложение взаимодействует с внешним сервером 1С. Суть метода заключается в том, чтобы перенести ресурсоемкую операцию по изменению размера изображения на сервер, где обычно доступно значительно больше оперативной памяти. Даже в случае автономного мобильного приложения, директива &НаСервере позволяет выделить выполнение ресурсоемких операций в более изолированный или оптимизированный контекст в рамках самого мобильного устройства, где может быть доступно больше памяти или другие политики управления ресурсами.

Разберем по шагам, как это реализовать:

  1. Создадим функцию в серверном общем модуле (или в модуле формы с директивой &НаСервере), которая будет выполнять изменение размера изображения.
  2. Передадим исходные данные картинки и желаемую максимальную ширину в эту серверную функцию.
  3. На сервере будет создана ОбрабатываемаяКартинка, выполнен ресайз и возвращена уже уменьшенная картинка.

Пример кода серверной функции:


&НаСервере
Функция ПолучитьМиниатюруНаСервере(ИсходныеДанныеКартинки, МаксимальнаяШирина) Экспорт
    // Создаем объект ОбрабатываемаяКартинка из исходных данных
    Обработка = Новый ОбрабатываемаяКартинка(ИсходныеДанныеКартинки);
    
    // Устанавливаем новый размер, сохраняя пропорции
    // Параметр 'Неопределено' для высоты означает автоматический расчет пропорций
    Обработка.УстановитьРазмер(МаксимальнаяШирина, Неопределено);
    
    // Возвращаем результат в виде Картинки
    Возврат Обработка.ПолучитьКартинку();
КонецФункции

Вызов этой функции из клиентского кода будет выглядеть так:


// Предполагается, что ИсходнаяКартинка это тип Картинка или ДвоичныеДанные
// Если ИсходнаяКартинка это ПутьКФайлу, то сначала нужно прочитать данные
Миниатюра = ПолучитьМиниатюруНаСервере(ИсходнаяКартинка, 200); // 200 - желаемая максимальная ширина

Этот подход гарантирует, что операция, требующая больших объемов памяти, будет выполнена в среде с достаточными ресурсами, полностью обходя проблему на мобильном устройстве.

Решение 2: Ограничение размера изображения до передачи в ОбрабатываемаяКартинка

Если ваше приложение является полностью автономным и не имеет возможности взаимодействовать с внешним сервером 1С, мы должны искать пути решения непосредственно на мобильном устройстве. Самый эффективный способ в этом случае – это уменьшать размер изображения до того, как оно будет передано в объект ОбрабатываемаяКартинка для дальнейшей обработки. Это значительно сократит объем данных, которые необходимо распаковывать в память.

Рассмотрим несколько сценариев:

При съемке камерой

Если мы получаем изображение путем съемки с камеры устройства, мы можем сразу ограничить его разрешение и качество. Для этого используются параметры метода СредстваМультимедиа.СделатьФотоснимок:

  1. ПараметрыСъемки.МаксимальнаяШирина и ПараметрыСъемки.МаксимальнаяВысота: Эти параметры позволяют задать максимальное разрешение снимка, который будет сделан камерой. Камера автоматически уменьшит изображение до указанных размеров.
  2. ПараметрыСъемки.КачествоФотокамеры: Этот параметр позволяет задать качество изображения в процентах (от 1% до 100%), что повлияет на степень сжатия JPEG и, соответственно, на размер файла и объем данных.

Пример кода:


// Создаем объект параметров съемки
ПараметрыСъемки = Новый ПараметрыСъемки;

// Ограничиваем максимальную ширину и высоту изображения
// до того, как оно будет получено с камеры
ПараметрыСъемки.МаксимальнаяШирина = 800; // Например, 800 пикселей
ПараметрыСъемки.МаксимальнаяВысота = 600; // Например, 600 пикселей

// Устанавливаем качество JPEG (если поддерживается камерой)
// Это уменьшит размер файла
ПараметрыСъемки.КачествоФотокамеры = 80; // 80% качества

// Оповещение для обработки результата съемки
Оповещение = Новый ОписаниеОповещения("ПослеСнимка", ЭтотОбъект);

// Вызываем метод для съемки фотоснимка
СредстваМультимедиа.СделатьФотоснимок(Оповещение, ПараметрыСъемки);

Таким образом, мы получаем уже уменьшенное изображение, которое с меньшей вероятностью вызовет ошибку нехватки памяти при дальнейшей обработке методом ОбрабатываемаяКартинка.УстановитьРазмер, если она все еще потребуется.

При выборе из галереи или файловой системы

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

Предварительное уменьшение (даунсэмплинг)

Общая практика мобильной разработки рекомендует "даунсэмплить" (уменьшать) большие изображения до того, как они будут загружены в память в полном разрешении, особенно если они будут отображаться в меньшем размере. Этот подход направлен на загрузку в память только той части данных изображения, которая действительно необходима для отображения или дальнейшей обработки. В платформе 1С это может быть реализовано путем ограничения размера изображения на этапе получения или путем предварительного сохранения изображения в меньшем качестве, как описано в следующем решении.

Решение 3: Сжатие JPEG до ресайза

Этот подход также применим для автономных мобильных приложений и позволяет уменьшить объем данных исходного изображения перед его полной декомпрессией и изменением размера. Идея заключается в том, чтобы сначала пересохранить исходное изображение в формат JPEG с более низким качеством, что уменьшит размер файла. Затем уже это, меньшее по объему, изображение будет передано в другой экземпляр ОбрабатываемаяКартинка для изменения размера.

Проанализируем шаги:

  1. Создадим первый объект ОбрабатываемаяКартинка из исходного изображения.
  2. Используем метод Записать для сохранения изображения во временный файл в формате JPEG с заданным качеством. Это действие само по себе уменьшает объем данных, которые будут обрабатываться дальше.
  3. Создадим второй объект ОбрабатываемаяКартинка уже из этого временного, сжатого файла.
  4. Выполним изменение размера с помощью УстановитьРазмер на втором объекте.

Пример кода:


// Предполагается, что ИсходныеДанныеКартинки это ДвоичныеДанные или Картинка
// 1. Создаем объект ОбрабатываемаяКартинка из исходных данных
ОбработкаПервая = Новый ОбрабатываемаяКартинка(ИсходныеДанныеКартинки);

// 2. Сначала сохраняем изображение как JPEG с низким качеством (например, 50%)
// Это уменьшит объем данных и потребление памяти
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".jpg");
ОбработкаПервая.Записать(ИмяВременногоФайла, Новый ПараметрыЗаписиJPEG(50)); 

// 3. Создаем второй объект ОбрабатываемаяКартинка уже из сжатого временного файла
ОбработкаВторая = Новый ОбрабатываемаяКартинка(ИмяВременногоФайла);

// 4. Теперь уже ресайзим это, меньшее по размеру, изображение
ОбработкаВторая.УстановитьРазмер(МаксимальнаяШирина, Неопределено);

// Получаем итоговую картинку
ИтоговаяКартинка = ОбработкаВторая.ПолучитьКартинку();

// Обязательно удаляем временный файл
УдалитьФайлы(ИмяВременногоФайла);

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

Дополнительные рекомендации по управлению ресурсами и кешированию

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

  1. Освобождение ресурсов: Крайне важно освобождать ресурсы, связанные с изображениями, когда они больше не нужны. Это особенно актуально, когда элементы интерфейса скрываются, или приложение переходит в фоновый режим. Корректное освобождение ресурсов позволяет операционной системе более эффективно управлять памятью и снижает вероятность принудительного завершения вашего приложения из-за нехватки памяти.
  2. Кеширование изображений: Для часто используемых изображений или изображений, которые должны быть доступны офлайн, рекомендуется использовать механизмы кеширования (как в оперативной памяти, так и на диске). Это предотвращает повторную загрузку, декомпрессию и обработку одних и тех же изображений, что значительно улучшает производительность приложения и снижает общее потребление памяти. Мы можем реализовать собственный механизм кеширования, сохраняя миниатюры или обработанные изображения в постоянной памяти устройства (например, в каталоге данных приложения) с привязкой к уникальному идентификатору исходного объекта.

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

← На главную