Understanding FMX Architecture

GPU-Accelerated UI and Styles

FMX relies heavily on GPU acceleration via Direct2D/OpenGL/Metal for rendering. Misconfigured styles or oversized visual trees lead to performance drops and GPU memory pressure.

Cross-Platform Abstraction

While FMX abstracts platform-specific controls, rendering inconsistencies often appear when default styles or input handlers behave differently on iOS vs. Android. These must be handled explicitly in code.

Common FMX Issues in Mobile Applications

1. Memory Leaks in Repeated Navigation

Improper use of TFrame or TForm without freeing causes memory to grow with each navigation.

procedure TMainForm.OpenNewFrame;
var NewFrame: TMyFrame;
begin
  NewFrame := TMyFrame.Create(Self);
  // BAD: no cleanup on close
end;

Solution:

NewFrame.DisposeOf;

2. Laggy UI on Android

Animations and timers sharing the main thread lead to UI lag. Use TThread.Queue or TTask.Run to offload non-UI logic.

3. Input Field Focus Bugs on iOS

Virtual keyboard resizing bugs or overlapped controls often stem from incorrect use of ContentBounds or lack of keyboard-aware layouts.

Diagnostic Workflow

1. Use Delphi Profiler and Memory Leak Reports

Enable ReportMemoryLeaksOnShutdown := True in debug builds. Analyze retained objects using FastMM or external profilers like AQTime.

2. Platform-Specific Logging

Use Log.d equivalents (e.g., OutputDebugString, NSLog, or Logcat) with conditional compilation:

{$IFDEF ANDROID}
Log.d('FMXDebug', 'Touch event received');
{$ENDIF}

3. Inspect Style Performance

Complex FMX styles degrade rendering. Use simple styles and disable animations on low-end devices.

Step-by-Step Fix: Frame Lifecycle Management

1. Always Free Dynamic Forms/Frames

Use DisposeOf or FreeAndNil when navigating away:

if Assigned(CurrentFrame) then
begin
  CurrentFrame.DisposeOf;
  CurrentFrame := nil;
end;

2. Avoid Recursive UI Updates

Timers updating UI recursively can freeze rendering. Always update UI via TThread.Synchronize or Queue.

3. Use Platform Services

For native behavior (e.g., status bar handling), use TPlatformServices interface to query and call OS-specific implementations safely.

Best Practices for Scalable FMX Mobile Apps

  • Keep visual trees shallow and avoid unnecessary nesting
  • Use native controls via FMX.Platform.iOS or Android APIs when performance is critical
  • Monitor GPU memory usage via platform tools (e.g., Instruments or GPU Inspector)
  • Structure navigation using a central controller that manages form/frame lifecycles
  • Profile all builds before release—performance issues vary drastically by device

Conclusion

Delphi FireMonkey offers a powerful and elegant way to build native cross-platform apps, but scalability and stability require disciplined resource management, attention to platform-specific quirks, and use of native threading models. By proactively diagnosing memory, rendering, and input issues, teams can avoid performance pitfalls and build professional-grade mobile apps that behave consistently across devices and platforms.

FAQs

1. Why does my FMX app consume more memory over time?

This is often due to unfreed forms/frames or retained references. Use DisposeOf and monitor with FastMM in debug builds.

2. How can I fix keyboard overlap issues on iOS?

Use a TVertScrollBox and adjust layout dynamically using the virtual keyboard service via IFMXVirtualKeyboardService.

3. Can FMX apps run smoothly on low-end Android devices?

Yes, if GPU usage is optimized. Avoid gradients, shadows, and animations, and reduce the number of live controls.

4. What's the difference between DisposeOf and Free?

DisposeOf ensures immediate object destruction on mobile platforms where ARC is used. Free only works reliably in non-ARC environments.

5. How do I debug crashes only occurring on Android?

Use adb logcat for real-time logs and symbolicate crash addresses. Add inline logging and isolate platform-specific code with compiler directives.