Jump to content

Viktor Akselrod

Administrators
  • Posts

    261
  • Joined

  • Last visited

  • Days Won

    45

Everything posted by Viktor Akselrod

  1. Здравствуйте. В этой статье мы поговорим об отладке приложений на iOS с помощью логирования (часть материала будет актуальна и для Android). Общие сведения. Логирование позволяет получать информацию о работе вашего приложении даже когда оно запущено без отладчика, при фатальных падениях приложения и тд. Особенно тема логирования актуальна для мобильных платформ, т.к. здесь, к сожалению, Delphi отладчик не может похвастаться такой же стабильностью и качеством, как отладчик для платформы Win32. Пошаговая отладка выливается в мучительное ожидание переходов, показываемые отладчиком значения могут не соответствовать действительности, либо вообще отсутствовать и тд. Важно! Логирование не заменяет полностью отладчик, а является вспомогательным инструментом. Условно механизм логирования можно разделить на источник логов и на утилиты для сборки и просмотра логов. Источники логов. Источником логов может быть как библиотека FGX Native, операционная система, так и само приложение (сообщения отправленные непосредственно разработчиком). На первые два варианта разработчик особо не может повлиять, поэтому более подробно остановимся на третьем варианте. Отправка сообщения в лог осуществляется с помощью кроссплатформенного класса TfgLog. Все методы TfgLog являются классовыми, а сам класс существует в единственном экземпляре - это означает, что для его использования нет необходимости создавать экземпляр класса. Основным методом класса является метод Log позволяющий отправить строку с указанным уровнем лога. /// <summary>Выводит в системный лог сообщение <c>AMessage</c> типа <c>ALogType</c>.</summary> class procedure Log(const ALevel: TfgLogLevel; const AMessage: string); Уровни лога определяют степень важности того или иного события. /// <summary>Уровень логирования.</summary> TfgLogLevel = (Trace, Debug, Info, Warning, Error, Fatal); Вы можете определить минимальный уровень лога с помощью свойства MinimumLevel, чтобы уменьшить количество выводимых в лог сообщений /// <summary> /// Указывает минимальный уровень лога, начиная с которого сообщение будет выводиться в системный лог. /// Порядок следования уровней: Debug, Info, Warning, Error, Fatal. Debug - самый низкий. /// </summary> /// <remarks> /// Для <c>DEBUG</c> конфигурации выводятся все типы сообщений по умолчанию. Для <c>RELEASE</c> сообщений выше /// <c>Info</c>. Если вы хотите видеть в релизной сборке вашего приложения и отладочные сообщения, то задайте /// значение <c>TfgLogLevel.Debug</c> в это свойство. Данная настройка распространяется только на этот класс. На /// логирование через сервис <c>IFGXLoggerService</c> она не действует. /// </remarks> class property MinimumLevel: TfgLogLevel read FMinimumLogLevel write FMinimumLogLevel; Для удобства у класса TfgLog есть набор одноименных методов, соответствующих каждому уровню лога. Например: /// <summary> /// Выводит в системный лог низкоуровневое отладочное сообщение <c>AMessage</c> для выполнения трассировки. /// </summary> class procedure Trace(const AMessage: string); overload; /// <summary>Выводит в системный лог отладочное сообщение <c>AMessage</c>.</summary> class procedure Debug(const AMessage: string); overload; В дополнение, у каждого такого метода существует перегруженная версия, принимающая во втором аргументе список параметров для отправки форматированной строки: /// <summary> /// Выводит в системный лог низкоуровневое отладочное сообщение <c>AMessage</c> с поддержкой форматирования <c>System.Format</c>. /// </summary> class procedure Trace(const AFormat: string; const Args: array of const); overload; /// <summary> /// Выводит в системный лог отладочное сообщение <c>AMessage</c> с поддержкой форматирования <c>System.Format</c>. /// </summary> class procedure Debug(const AFormat: string; const Args: array of const); overload; Сборка и просмотр логов. Для просмотра логов с iOS используется утилита, работающая на компьютере под управлением macOS под названием Console. Для просмотра логов с Android используется утилита работающая на компьютере под управлением Windows под названием logcat. Частично логирование в контексте Android было затронуто ранее в Уроке 1 «Настройка окружения, Hello World приложение и логирование». В данной статье мы сосредоточимся на утилите Console. Важно! Подразумевается, что вы имеете настроенную связку: компьютер под управлением macOS к которому подключено устройство с iOS. Исходные данные: на устройстве с iOS установлено приложение под названием TestLogging, которое имеет только две кнопки, при нажатии на которые отправляются сообщения в системный лог. Код выглядит следующим образом: uses System.SysUtils, FGX.Application, FGX.Dialogs, FGX.Log; procedure TFormMain.fgButton1Tap(Sender: TObject); begin TfgLog.Debug('Hello from TfgLog.Debug'); end; procedure TFormMain.fgButton2Tap(Sender: TObject); begin TfgLog.Info('Hello from TfgLog.Info'); end; Теперь перейдем непосредственно к утилите сбора и просмотра логов. Запустите утилиту Console которая находится в Launchpad -> Other. Главное окно состоит из меню навигации в левой части (1), тулбара с органами управления вверху (2) и списка логов, который занимают остальную клиентскую область окна (3). Теперь выбираем в меню навигации слева устройство из списка Devices с которого мы хотим получать логи. В нашем случае это устройство с именем IPhone. Теперь запускаем сбор логов с помощью кнопки тулбара Start или кликнув по ссылке Start streaming находящейся в центре списка логов. После старта список логов будет быстро наполняться сообщениями. Список логов представляет собой таблицу с настраиваемыми колонками. Вы можете настроить видимость колонок по своему усмотрению с помощью контекстного меню, вызываемого по клику на заголовки таблицы. Самая полезная для нас информация находится в колонке Message - это непосредственно текстовое сообщение лога. Более удобно текстовое сообщение просматривать в окне детальной информации, которое можно открыть двойным кликом по строке списка логов, либо с помощью кнопки тулбара Info. Теперь запустим наше тестовое приложение и последовательно нажмем обе кнопки. Лог содежит тысячи записей, среди которых практически невозможно найти интересующие нас строки. Для решения этой задачи существуют фильтры, которые располагаются в правой части тулбара. Механизм фильтрация довольно гибкий и позволяет комбинировать несколько фильтров по разным колонкам с учетом выбранного способа сравнения (частичное или полное совпадение включая вариант с отрицанием). Важно! Фильтрация осуществляется без учета регистра. Есть несколько способов, как можно отобразить только те строки, которые относятся к нашему приложению. Самый простой вариант - отфильтровать по имени процесса. Вводим фразу TestLogging в окно Search и нажимаем клавишу Enter на клавиатуре. По-умолчанию создастся фильтр с типом Any и способом сравнения Containts. Теперь изменим тип фильтра с Any на Process, a Containts на Equal, что означает, что в списке логов останутся только те строки, текст которых в колоноке равен TestLogging. На скриншоте видно, как строки переданные самой библиотекой FGX Native, так и те, которые были отправлены после нажатия на кнопки (отмечены стрелками). Теперь вы можете изучать собранную информацию просматривая сообщения. При необходимости вы можете сохранить выделенные строки в буфер обмена, либо передать их в другое приложение к помощью кнопки тулбара Share. Логирование - мощный инструмент, который может пригодиться в самых трудных и запутанных ситуациях при поиске ошибок. Спасибо за внимание и удачной отладки!
  2. A demo example will be posted right after the next release, because it uses some of the new functionality not available in the current release.
  3. Стала доступна запись вэбинара Демо-пример будет выложен сразу после очередного релиза, т.к. в нем используется часть нового функционала недоступного в текущем релизе.
  4. Пример мобильного приложения из вебинара так же будет доступен на форуме несколько позже.
  5. Hello. We are sorry. We have some technical problems. We try to solve them as quickly as possible. Thank you.
  6. Здравствуйте. Еще одним нововведением в релизе 1.11.2.0, помимо TfgPopup, является работа с пикселями ("сырыми данными") TfgBitmap. Доступ к пикселям открывает практически неограниченные возможности для обработки и анализа изображений. Так же в новом релизе будет добавлена возможность поворота изображения. Воможность работы с пикселями TfgBitmap. Общие сведения. Обращение к сырым данным возможно только между парными вызовами TfgBitmap.MapData и TfgBitmap.UnmapData, которые могут быть знакомы пользователям FMX. Однако, важным отличием от FMX, является отсутствие уровня доступа (только чтение/только запись/чтение и запись). Это сделано с целью ускорения доступа, т. к. работа осуществляется непосредственно с данными нативного изображения без создания промежуточного буфера. В результате вызова TfgBitmap.MapData вы получаете интерфейс IFGXBitmapData (более подробно рассмотрен ниже), который предоставляет доступ к пикселям изображения. Важно! Актуальность объекта IFGXBitmapData гарантирована только между вызовами TfgBitmap.MapData и TfgBitmap.UnmapData, т.к. используется "натуральный" интерфейс без подсчета ссылок. Важно! Во время доступа к сырым данным операции по изменению TfgBitmap запрещены (например загрузка из различных источников, изменение размера, масштаба, поворота и тд). Типичный пример работы с сырыми данными изображения: var LBitmapData: IFGXBitmapData; begin if LBitmap.MapData(LBitmapData) then try // Работа с пикселями изображения. finally LBitmap.UnmapData; end; end; Важно! Всегда проверяйте результат выполнения TfgBitmap.MapData во избежание непредвиденных последствий. Повторный вызов TfgBitmap.MapData для уже спроецированных данных вернет ложь. Интерфейс IFGXBitmapData. Интерфейс IFGXBitmapData предоставляет информацию об изображении (Info, Width, Height), а так же позволяет манипулировать пикселями изображения (RawData, Scanlines, Pixels, Colors). Информация о битмапе TfgBitmapInfo: BitsPerPixel — количество бит на один пиксель изображения. К примеру, для одного из самых распространенных вариантов ARGB, этот параметр будет равен 32-ум битам. BytesPerRow — количество байт на одну строку изображения. Обычно это Width * BitsPerPixel, но, в случае применения выравнивания для ускорения доступа к данным, значение может быть иным. PixelFormat — формат пикселя изображения. В отличии от фиксированного формата ARGB присущего стандартному типу TAlphaColor, нативные изображения могут иметь различный состав и порядок следования цветовых компонентов. Возможные комбинации этих параметров отражает тип TfgBitmapPixelFormat. TfgBitmapPixelFormat = (Unknown, Alpha, Gray, ARGB, RGBA, RGB, RGB24); IsAlphaPremultiplied — флаг, показывающий являются ли компоненты пикселя предварительно помноженными на компоненту прозрачности. Интерфейс IFGXBitmapData содержит несколько вариантов доступа к самим данным, которые отличаются по степени абстракции: RawData — самый низкий уровень. Возвращает указатель на начало данных. Размер данных можно узнать из свойства RawDataSize. Scanlines — указатель на переданную в аргументе строку. Размер данных строки в байтах можно получить из Info.BytesPerRow, количество пикселей в строке — Width. Pixels — указатель на пиксель. В аргументах передаются координаты пикселя. Размер пикселя в битах можно получить из Info.BitsPerPixel. Colors — цвет пикселя в стандартном формате TAlphaColor. В аргументах передаются координаты пикселя. Как правило, для достижения максимальной скорости при последовательном сканировании пикселей изображения, вместо индексного доступа (Pixels, Colors) используют инкрементный перебор начиная с некой точки отсчёта (начало изображения RawData, начало определенной строки Scanlines[Y], определенный пиксель Pixelsх[X, Y]) . В этом случае вы получаете указатель, вместо привычного TAlphaColor. Для преобразования данных по указателю в тип TAlphaColor вам могут пригодиться готовые варианты для конвератции, которые находятся в хэлпере TAlphaColorHelper из модуля FGX.Types.Color. Для формирования TAlphaColor из сырых данных используйте перегруженный конструктор: constructor Create(const APixel: PByte; const APixelFormat: TfgBitmapPixelFormat; const AIsAlphaPremultiplied: Boolean); overload; Для обратной операции подойдет метод: /// <summary>Записывает текущий цвет по адресу <c>APixel</c> с учетом опций <c>APixelFormat</c> и <c>AIsAlphaPremultiplied</c>.</summary> procedure ToPixel(const APixel: PByte; const APixelFormat: TfgBitmapPixelFormat; const AIsAlphaPremultiplied: Boolean); Реальный пример работы с сырыми данными. Ниже приведен кусок кода из нового демонстрационного примера "Графика" -> "TfgBitmap - работа с "сырыми" данными" в котором изображение преобразуется из цветного в оттенки серого. uses FGX.Types.Color, FGX.Canvas.Types, FGX.Canvas.Types, FGX.Assets, FGX.Assets.BitmapSet; procedure TFormMain.fgButton1Tap(Sender: TObject); var LBitmapSet: TfgAssetBitmapSet; LBitmap: TfgBitmap; LBitmapData: IFGXBitmapData; I, J: Integer; begin if not TfgAssetsManager.Current.Find<TfgAssetBitmapSet>('Image', LBitmapSet) then Exit; LBitmap := LBitmapSet.GetBitmap; if LBitmap.MapData(TfgBitmapDataAccessMode.ReadWrite, LBitmapData) then try for J := 0 to LBitmap.Height - 1 do for I := 0 to LBitmap.Width - 1 do LBitmapData.Colors[I, J] := LBitmapData.Colors[I, J].ToGrayscale; finally LBitmap.UnmapData; end; fgPaintBox1.Invalidate; end; Повороты изображения. Благодаря добавлению работы с сырыми данными появилась возможность с легкостью вращать изображения. Поворот осуществляется с помощью метода TfgBitmap.Rotate: /// <summary>Поворачивает изображение на указанный угол.</summary> procedure Rotate(const AAngle: TfgRotation); В качестве аргумента передается одно из предустановленных значений угла поворота. TfgRotation = (Rotation0 = 0, Rotation90 = 90, Rotation180 = 180, Rotation270 = 270); Поворот осуществляется по часовой стрелке. Благодарим за внимание и приятного коддинга 🙂.
  7. Здравствуйте. Ошибка подтверждена. Ваша сообщение переведено в баг трекер.
  8. Здравствуйте. Проблема в iOS мосте (RTL) на Rio. В настоящий момент мы ищем решение.
  9. Yes. iOS profile will be created anyway. These checkboxes are not responsible for creating an iOS profile. If you are not interested in the iOS profile, just leave the default checkboxes.
  10. Hello @MoreWood We have fixed the translation in the next release. Correct translation: You must select at least one type of devices for the iOS platform. Thank you.
  11. Здравствуйте. Продублировал ваш запрос на багтрекер. Спасибо.
  12. Здравствуйте. Проверим информацию. Спасибо за отчет.
  13. All right. Still need a fix from our side. Thank you
  14. Спасибо за ответ. Проверим на вашем окружении.
  15. Hello. TfgAnimationHelper.HideForm(Self, [TfgAnimationOption.ReleaseOnFinish]) - this call does not close immediately form. first plays the animation, then enqueues the deletion and only then deletes the form. during this time fgBarcodeScanDetected may fire several more times. so it is important to prevent for double form destruction.
  16. Besides the bugfix, please fix the logic as follows: procedure TFCamera.CloseForm (OK: boolean); begin if FCamera = nil then Exit; if OK and Assigned (FOnReadCode) and (fgLabCode.Text <> '-') then FOnReadCode (fgLabCode.Text); TfgAnimationHelper.HideForm (Self, [TfgAnimationOption.ReleaseOnFinish]); FCamera: = nil; end; fgBarcodeScanDetected and TfgAnimationHelper.HideForm both are called asynchronously. TfgAnimationHelper.HideForm with TfgAnimationOption.ReleaseOnFinish flag should not be allowed to call more than once, otherwise it will lead to double destruction of the form and memory corruption.
  17. Здравствуйте. Подтверждаю проблему. Это не воспроизводится. Вы можете привести больше деталей относительно окружения? Проявляется ли в пустом проекте?
  18. I have reproduced the problem. Thanks for the report.
  19. Hello. There is currently no cross platform method to display PDF using FGX Please check this thread , it can help you. Thank you.
×
×
  • Create New...