Search code examples
androidperformancedelphifiremonkeyandroid-timer

Why is a Timer on Android more accurate when a finger is touching the screen?


In Delphi 10.1 Berlin, I'm making an Android app. I created a timer like this:

fTimer := TTimer.Create(nil);
fTimer.Interval := 1;
fTimer.OnTimer := OnTimer;
fTimer.Enabled := True;

In the OnTimer event, I simply do this:

procedure TMyForm.OnTimer(Sender: TObject);
begin
  MyStopWatch.Stop;
  Inc(acounter);
  if acounter mod 1000 = 0 then 
    allog('delay', FloatToStr(xStopWatch.Elapsed.TotalMilliseconds));
  MyStopWatch := TStopWatch.StartNew;
end;

When I launch the app, the OnTimer event is fired every 10 ms instead of every 1 ms. However, if I touch the screen and move my finger around it, the event is fired every 1.3-1.5 ms instead.

Can someone explain this strange behavior to me?

Why is the app (or at least the timer) more reactive when my finger is touching the screen? How do I make the app always be this reactive?

About the remark of J..

it's not baterry life i think (but i m not sure) because if I use a thread instead of a timer like this :

TThread.createAnonymousThread(
  procedure
  var MyStopWatch: TstopWatch;
      acounter: integer;
  begin

    acounter := 0;
    MyStopWatch :=  TStopWatch.StartNew;

    while True do begin
      TThread.synchronize(nil,
        procedure
        begin
          MyStopWatch.Stop;
          Inc(acounter);
          if acounter mod 1000 = 0 then
            allog('delay', FloatToStr(MyStopWatch.Elapsed.TotalMilliseconds));
          MyStopWatch := TStopWatch.StartNew;
        end);
      sleep(1);
    END;

  end).start;

Then it's work ok, the event is fired every 2 ms (without TThread.synchronize every 1ms), and this finger or not on the screen.


Solution

  • Unlike VCL's TTimer, FMX's TTimer on Android is quite inefficient.

    When the timer interval elapses, Android notifies FMX using a callback function (in the Androidapi.Timer unit). That callback is called by a worker thread, and it pushes the timer into a thread-safe queue (in the FMX.Platform.Android unit).

    When the main UI thread checks for pending UI messages periodically, it also checks the timer queue, and if any timers are queued then their OnTimer event handlers are called (in the order that they have been queued).

    However, if there are no pending UI messages, FMX may delay checking the timer queue! And then once all UI messages have been processed, there may be a delay before events are processed again. It all depends on the internal state of the FMX app.

    So, there is no guarantee whatsoever that a 1 ms timer will fire its OnTimer event handler anywhere near 1 ms intervals. And it might also explain why an increase in UI activity can allow the OnTimer event to fire more often, because UI messages are being processed more often.

    This differs from VCL's TTimer, which is based on the WM_TIMER UI message, which is a low-priority message that is only generated when there are no other UI messages pending. And when it is generated, it gets dispatched directly to the TTimer's internal window, there is no additional queuing involved to add extra layers of overhead. But, an increase in UI activity will slow down the TTImer.OnTimer event from firing, not speed it up.


    In the case of TThread.Synchronize(), it actively notifies the main UI thread whenever a pending sync request needs to be handled, thus allowing the main UI thread to check for and execute sync'ed procedures sooner rather than later.