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.