FireMonkey 2 Under The Hood Changes: PlatformServices

By: Darren Kosinski

Abstract: Blog post from Darren Kosinski on FireMonkey FM2

This is a copy of a blog post from http://blogs.embarcadero.com/darrenkosinski/2012/10/01/51/. If you have comments or feedback, please post comments on the original blog post.

    FireMonkey 2 Under The Hood Changes: PlatformServices

RAD Studio XE3 introduces the next evolution of the FireMonkey framework: FM2. The What’s New in FM2 page highlights a lot of the new features. I wanted to point out some under the hood changes that are also significant.

    Platform

One of the key concepts at the base of a cross-platform framework is providing an abstraction of the runtime environment, the operating system and hardware. In the original release of FireMonkey in RAD Studio XE2, this abstraction was provided by an abstract class, TPlatform. Each supported platform (Windows, Mac OS X and iOS) had a concrete implementation of this abstract class and it was accessed via a global Platform variable.

When the runtime platforms have similar features and capabilities, this approach is reasonable. As platform features start to diverge, however, this approach makes it difficult for a developer to know which parts of the abstraction are implemented on the runtime environment. This divergence was evident in the number of no-op implementations in the various platform units.

    PlatformServices

In FM2, this abstraction has been significantly rewritten. Instead of a single abstract class, FM2 now has a registry of platform services, TPlatformServices (found in FMX.Platform.pas):

  TPlatformServices = class
  private
    FServicesList: TDictionary<TGUID, IInterface>;
    FGlobalFlags: TDictionary<string, Boolean>;
    class var FCurrentPlatform: TPlatformServices;
    class function GetCurrent: TPlatformServices; static;
  public
    constructor Create;
    destructor Destroy; override;
    class procedure UnInitialize;
    procedure AddPlatformService(const AServiceGUID: TGUID; const AService: IInterface);
    procedure RemovePlatformService(const AServiceGUID: TGUID);
    function GetPlatformService(const AServiceGUID: TGUID): IInterface;
    function SupportsPlatformService(const AServiceGUID: TGUID): Boolean; overload;
    function SupportsPlatformService(const AServiceGUID: TGUID;
      out AService: IInterface): Boolean; overload;
    property GlobalFlags: TDictionary<string, Boolean> read FGlobalFlags;
    class property Current: TPlatformServices read GetCurrent;
  end;

This class allows services to be added and removed from the registry, with the methods AddPlatformService and RemovePlatformService, respectively. The functions SupportsPlatformService provide a way for developers to query the registry to determine whether or not a particular service is supported at runtime. These functions were written to be similar to the Delphi RTL Supports functions for working with Delphi interfaces.

    Services

So what is a platform service? It is simply an interface which defines some functionality which may or may not be implemented on a particular runtime platform. For example, this is the definition of the IFMXApplicationServices interface which defines the basic operations expected of an Application object:

  IFMXApplicationService = interface(IInterface)
    ['{EFBE3310-D103-4E9E-A8E1-4E45AB46D0D8}']
    procedure Run;
    procedure Terminate;
    function HandleMessage: Boolean;
    procedure WaitMessage;
    function GetTitle: string;
  end;

The FireMonkey TApplication object uses this service to control the application. FireMonkey cannot do very much without this service, so an implementation is provided for every runtime environment.

There are a number of other services which are not as essential. An on-screen keyboard is a good example. Functions to support an on-screen keyboard are provided by the platform service interface IFMXVirtualKeyboardService:

  IFMXVirtualKeyboardService = interface(IInterface)
    ['{BB6F6668-C582-42E4-A766-863C1B9139D2}']
    function ShowVirtualKeyboard(AControl: TFmxObject): Boolean;
    function HideVirtualKeyboard: Boolean;
    function GetVirtualKeyBoardState: TVirtualKeyBoardState;
    property VirtualKeyBoardState: TVirtualKeyBoardState read GetVirtualKeyBoardState;
  end;

To support the touch-oriented features of Windows 8, FM2 implements this service on the Windows platform. The service is not implemented for Mac OS X, however. Before a developer tries to use an on-screen keyboard, it is important to verify whether the service is supported or not, using code like this:

if TPlatformServices.Current.SupportsPlatformService(IFMXVirtualKeyboardService) then

    Platform Growth and Advanced Uses

Changing the platform abstraction to a registry provides a much more powerful and flexible mechanism which will allow FireMonkey to be implemented on more platforms (for example, those mentioned in the RAD Studio Mobile Roadmap).

This mechanism provides a lot of power to developers to tailor applications to specific needs as well. For example, if a developer needs to provide an on-screen keyboard for a Mac OS X-based kiosk application, the developer can implement the IFMXVirtualKeyboardService interface and register it to get the FireMonkey support for on-screen keyboards.

It is also possible to unregister a service that FireMonkey does implement and replace it with a new implementation of the service which is tailored to fit the needs of a specialized application environment.

Server Response from: ETNASC03