При разработке автономных мобильных приложений на платформе 1С, особенно когда требуется работа с графикой, мы можем столкнуться с неожиданными проблемами, которые не проявляются при разработке на десктопе. Одной из таких распространенных трудностей является ошибка "Недостаточно памяти для загрузки картинки", которая возникает при попытке изменить размер изображения с использованием объекта ОбрабатываемаяКартинка и метода УстановитьРазмер на мобильной платформе.
Рассмотрим ситуацию, когда возникает данная ошибка. Мы хотим получить миниатюру (пиктограмму) изображения для отображения в пользовательском интерфейсе, например, в автономном мобильном приложении при отсутствии интернета. Исходные изображения могут быть достаточно большими, загружаясь, например, из облака при наличии подключения. Для уменьшения размера итогового файла и экономии ресурсов мы пытаемся сжать исходное изображение в момент записи в реквизит справочника или другую структуру данных. Код, который мы используем, выглядит примерно так:
Обработка = Новый ОбрабатываемаяКартинка(ИсходнаяКартинка);
Обработка.УстановитьРазмер(МаксимальнаяШирина, Неопределено); // Здесь возникает ошибка на мобильной платформе
ИтоговаяКартинка = Обработка.ПолучитьКартинку;
На десктопе этот код отрабатывает без проблем, но на мобильной платформе (как в эмуляторе, так и на реальном устройстве под Android) мы получаем ошибку "Недостаточно памяти для загрузки картинки" именно в момент вызова метода УстановитьРазмер.
Выясним причину такого поведения и предложим несколько рабочих решений.
Основная причина этой ошибки заключается в том, что объект ОбрабатываемаяКартинка на мобильной платформе, при вызове метода УстановитьРазмер, пытается полностью распаковать исходное изображение в оперативную память устройства. В отличие от настольных систем, мобильные устройства обладают ограниченным объемом оперативной памяти. Несжатое изображение, например, в формате RGBA, может занимать в несколько раз больше места, чем его исходный файл на диске. Например, фотография размером 4000x3000 пикселей в несжатом виде может потребовать около 48 МБ памяти. Если приложение уже потребляет значительную часть доступной памяти или само устройство имеет скромные ресурсы, попытка выделить такой объем памяти может привести к ошибке "Недостаточно памяти".
Давайте рассмотрим, как мы можем решить эту проблему, предлагая несколько эффективных подходов.
Это наиболее надежный и рекомендуемый способ, если ваше мобильное приложение взаимодействует с внешним сервером 1С. Суть метода заключается в том, чтобы перенести ресурсоемкую операцию по изменению размера изображения на сервер, где обычно доступно значительно больше оперативной памяти. Даже в случае автономного мобильного приложения, директива &НаСервере позволяет выделить выполнение ресурсоемких операций в более изолированный или оптимизированный контекст в рамках самого мобильного устройства, где может быть доступно больше памяти или другие политики управления ресурсами.
Разберем по шагам, как это реализовать:
&НаСервере), которая будет выполнять изменение размера изображения.ОбрабатываемаяКартинка, выполнен ресайз и возвращена уже уменьшенная картинка.Пример кода серверной функции:
&НаСервере
Функция ПолучитьМиниатюруНаСервере(ИсходныеДанныеКартинки, МаксимальнаяШирина) Экспорт
// Создаем объект ОбрабатываемаяКартинка из исходных данных
Обработка = Новый ОбрабатываемаяКартинка(ИсходныеДанныеКартинки);
// Устанавливаем новый размер, сохраняя пропорции
// Параметр 'Неопределено' для высоты означает автоматический расчет пропорций
Обработка.УстановитьРазмер(МаксимальнаяШирина, Неопределено);
// Возвращаем результат в виде Картинки
Возврат Обработка.ПолучитьКартинку();
КонецФункции
Вызов этой функции из клиентского кода будет выглядеть так:
// Предполагается, что ИсходнаяКартинка это тип Картинка или ДвоичныеДанные
// Если ИсходнаяКартинка это ПутьКФайлу, то сначала нужно прочитать данные
Миниатюра = ПолучитьМиниатюруНаСервере(ИсходнаяКартинка, 200); // 200 - желаемая максимальная ширина
Этот подход гарантирует, что операция, требующая больших объемов памяти, будет выполнена в среде с достаточными ресурсами, полностью обходя проблему на мобильном устройстве.
Если ваше приложение является полностью автономным и не имеет возможности взаимодействовать с внешним сервером 1С, мы должны искать пути решения непосредственно на мобильном устройстве. Самый эффективный способ в этом случае – это уменьшать размер изображения до того, как оно будет передано в объект ОбрабатываемаяКартинка для дальнейшей обработки. Это значительно сократит объем данных, которые необходимо распаковывать в память.
Рассмотрим несколько сценариев:
Если мы получаем изображение путем съемки с камеры устройства, мы можем сразу ограничить его разрешение и качество. Для этого используются параметры метода СредстваМультимедиа.СделатьФотоснимок:
ПараметрыСъемки.МаксимальнаяШирина и ПараметрыСъемки.МаксимальнаяВысота: Эти параметры позволяют задать максимальное разрешение снимка, который будет сделан камерой. Камера автоматически уменьшит изображение до указанных размеров.ПараметрыСъемки.КачествоФотокамеры: Этот параметр позволяет задать качество изображения в процентах (от 1% до 100%), что повлияет на степень сжатия JPEG и, соответственно, на размер файла и объем данных.Пример кода:
// Создаем объект параметров съемки
ПараметрыСъемки = Новый ПараметрыСъемки;
// Ограничиваем максимальную ширину и высоту изображения
// до того, как оно будет получено с камеры
ПараметрыСъемки.МаксимальнаяШирина = 800; // Например, 800 пикселей
ПараметрыСъемки.МаксимальнаяВысота = 600; // Например, 600 пикселей
// Устанавливаем качество JPEG (если поддерживается камерой)
// Это уменьшит размер файла
ПараметрыСъемки.КачествоФотокамеры = 80; // 80% качества
// Оповещение для обработки результата съемки
Оповещение = Новый ОписаниеОповещения("ПослеСнимка", ЭтотОбъект);
// Вызываем метод для съемки фотоснимка
СредстваМультимедиа.СделатьФотоснимок(Оповещение, ПараметрыСъемки);
Таким образом, мы получаем уже уменьшенное изображение, которое с меньшей вероятностью вызовет ошибку нехватки памяти при дальнейшей обработке методом ОбрабатываемаяКартинка.УстановитьРазмер, если она все еще потребуется.
Если изображение выбирается из галереи или файловой системы устройства (например, с помощью НачатьПомещениеФайла), также можно задать ограничения на максимальный размер или качество изображения, если метод позволяет это делать. Однако, стандартные методы работы с файлами могут не предоставлять таких тонких настроек, как методы для работы с камерой. В таких случаях, если исходное изображение все равно оказывается слишком большим, мы можем рассмотреть предварительное уменьшение (даунсэмплинг).
Общая практика мобильной разработки рекомендует "даунсэмплить" (уменьшать) большие изображения до того, как они будут загружены в память в полном разрешении, особенно если они будут отображаться в меньшем размере. Этот подход направлен на загрузку в память только той части данных изображения, которая действительно необходима для отображения или дальнейшей обработки. В платформе 1С это может быть реализовано путем ограничения размера изображения на этапе получения или путем предварительного сохранения изображения в меньшем качестве, как описано в следующем решении.
Этот подход также применим для автономных мобильных приложений и позволяет уменьшить объем данных исходного изображения перед его полной декомпрессией и изменением размера. Идея заключается в том, чтобы сначала пересохранить исходное изображение в формат JPEG с более низким качеством, что уменьшит размер файла. Затем уже это, меньшее по объему, изображение будет передано в другой экземпляр ОбрабатываемаяКартинка для изменения размера.
Проанализируем шаги:
ОбрабатываемаяКартинка из исходного изображения.Записать для сохранения изображения во временный файл в формате JPEG с заданным качеством. Это действие само по себе уменьшает объем данных, которые будут обрабатываться дальше.ОбрабатываемаяКартинка уже из этого временного, сжатого файла.УстановитьРазмер на втором объекте.Пример кода:
// Предполагается, что ИсходныеДанныеКартинки это ДвоичныеДанные или Картинка
// 1. Создаем объект ОбрабатываемаяКартинка из исходных данных
ОбработкаПервая = Новый ОбрабатываемаяКартинка(ИсходныеДанныеКартинки);
// 2. Сначала сохраняем изображение как JPEG с низким качеством (например, 50%)
// Это уменьшит объем данных и потребление памяти
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".jpg");
ОбработкаПервая.Записать(ИмяВременногоФайла, Новый ПараметрыЗаписиJPEG(50));
// 3. Создаем второй объект ОбрабатываемаяКартинка уже из сжатого временного файла
ОбработкаВторая = Новый ОбрабатываемаяКартинка(ИмяВременногоФайла);
// 4. Теперь уже ресайзим это, меньшее по размеру, изображение
ОбработкаВторая.УстановитьРазмер(МаксимальнаяШирина, Неопределено);
// Получаем итоговую картинку
ИтоговаяКартинка = ОбработкаВторая.ПолучитьКартинку();
// Обязательно удаляем временный файл
УдалитьФайлы(ИмяВременногоФайла);
Этот подход может помочь, если даже уменьшенное в качестве изображение все еще может быть обработано в рамках доступной памяти мобильного устройства.
Помимо представленных решений, мы должны всегда помнить об эффективном управлении ресурсами при работе с изображениями на мобильных платформах:
Понимание того, что изображение в памяти декомпрессируется и занимает значительно больше места, чем его файл на диске, является критически важным для оптимизации работы с графикой на мобильной платформе 1С. Применение указанных выше подходов поможет нам избежать проблем с нехваткой памяти и обеспечит более стабильную и эффективную работу наших мобильных приложений.