FireMonkey 2 Under The Hood Changes: PlatformServices

Posted by on in Blogs
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.


Comments

  • Guest
    Vsevolod Leonov Tuesday, 2 October 2012

    [You wrote:] 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.

    [regarding] IFMXVirtualKeyboardService
    Can I develop and plug-in my onw virtual keyboard?

  • Guest
    Darren Kosinski Tuesday, 2 October 2012

    @Vsevolod Leonov: Yes, you could. FireMonkey's control of the virtual keyboard is defined by the IFMXVirtualKeyboardService interface.

  • Guest

    [...] Kosinski has blogged about “TPlatformServices” class recently and explains how to query in code this class for a specific interface. The way to determine [...]

  • Guest
    tanie noclegi wrocław Thursday, 18 October 2012

    tanie noclegi wrocław...

    Darren Kosinski : FireMonkey 2 Under The Hood Changes: PlatformServices...

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: