【问题标题】:Why is a Timer on Android more accurate when a finger is touching the screen?为什么当手指触摸屏幕时,Android 上的 Timer 会更准确?
【发布时间】:2017-08-29 22:40:09
【问题描述】:

在 Delphi 10.1 Berlin 中,我正在制作一个 Android 应用程序。我创建了一个这样的计时器:

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

OnTimer 事件中,我只是这样做:

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;

当我启动应用程序时,OnTimer 事件每 10 毫秒触发一次,而不是每 1 毫秒触发一次。 但是,如果我触摸屏幕并在其周围移动手指,该事件会改为每 1.3-1.5 毫秒触发一次。

谁能向我解释一下这种奇怪的行为?

为什么当我的手指触摸屏幕时,应用程序(或至少是计时器)的反应性更强?如何让应用始终如此被动?

关于J..的言论

我认为这不是电池寿命(但我不确定),因为如果我使用线程而不是像这样的计时器:

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;

然后就ok了,每2ms触发一次事件(没有TThread.synchronize每1ms),这个手指还是不在屏幕上。

【问题讨论】:

  • 计时器从不用于记录时间 - 不要让这个名字欺骗了你。 Interval 是执行之间的最小延迟。计时器绝不会被锁定到确切的时间。如果您有时间敏感的任务,请使用系统时间,而不是计时器的模糊延迟。
  • @JerryDodge:我不想要完美的精度,但是在手指在屏幕上的 1ms 到没有手指在屏幕上的 10ms 之间,它们之间的差距是巨大的!例如,这会导致滚动问题(delphi 滚动计算是在计时器顶部进行的)。但真正的问题是:当应用程序对屏幕上的手指更具反应性时,最重要的是如何让它始终像这样反应?
  • @Victoria:我不明白?你做了什么研究?我也对这个问题进行了一些研究,但一无所获。我只有这个账号...
  • 移动设备只做必要的就可以节省电量。当您直接与他们互动时,他们的反应更快。如果您故意规避平台的本机电源管理策略,那么您最终会得到一个会不必要地耗尽用户电池的应用程序。有更好的方法来解决您的实际问题。
  • @J... 我刚刚更新了关于您的评论的问题。你能看到吗?

标签: android performance delphi firemonkey android-timer


【解决方案1】:

与 VCL 的 TTimer 不同,FMX 在 Android 上的 TTimer 效率很低。

当计时器间隔结束时,Android 会使用回调函数(在Androidapi.Timer 单元中)通知 FMX。该回调由工作线程调用,并将计时器推入线程安全队列(在FMX.Platform.Android 单元中)。

当主 UI 线程定期检查待处理的 UI 消息时,它还会检查计时器队列,如果有任何计时器排队,则调用它们的 OnTimer 事件处理程序(按照它们排队的顺序)。

但是,如果没有待处理的 UI 消息,FMX 可能会延迟检查计时器队列!然后,一旦处理了所有 UI 消息,在再次处理事件之前可能会有延迟。这完全取决于 FMX 应用的内部状态。

因此,无法保证 1 毫秒计时器会在接近 1 毫秒间隔的任何地方触发其 OnTimer 事件处理程序。它也可以解释为什么 UI 活动的增加可以让OnTimer 事件更频繁地触发,因为 UI 消息被更频繁地处理。

这与 VCL 的 TTimer 不同,后者基于 WM_TIMER UI 消息,这是一条低优先级消息,仅在没有其他待处理的 UI 消息时生成。并且当它生成时,它被直接分派到TTimer 的内部窗口,不需要额外的排队来增加额外的开销层。但是,UI 活动的增加会减慢TTImer.OnTimer 事件的触发速度,而不是加快它的速度。


TThread.Synchronize() 的情况下,每当需要处理待处理的同步请求时,它都会主动通知主 UI 线程,从而允许主 UI 线程尽早检查并执行同步的过程。

【讨论】:

  • 亲爱的@RemyLebeau,我不明白,你说“但是,如果没有待处理的 UI 消息,FMX 可能会延迟检查计时器队列!” ...我阅读了 TPlatformAndroid.InternalProcessMessages 的源代码,并且在每个循环上调用 TTimerManager.Current.CheckSynchronize 是接缝,它与任何 UI 消息无关...主循环只是重复 InternalProcessMessages 直到 FAndroidApp ^。销毁请求 0; ...据我所知,没有什么是延迟的吗?
  • 也许我错了(我还没有调试它以确定),但是我阅读InternalProcessMessages()的源代码的方式,循环顶部等待超时在执行其余循环代码之前到达的新 UI 消息。当 FMX 没有做更高优先级的事情时,它是一个低优先级轮询,因此轮询可能需要一些时间。这就是为什么我认为当 UI 不忙时计时器队列会延迟。
  • 我也没有调试,只是阅读了源代码,我发现它没有在等待,而是只是“检查”它们是否是新的 ui 消息。如果他们没有新的 ui 消息,那么就去检查同步。所以在某种程度上我可以说 UI 消息通常甚至会减慢计时器事件的触发速度
  • 不,对不起,你是对的!等待 10 毫秒的 ui 消息......该死的,这是一个如此丑陋的想法!
  • 好的,现在我研究了很多想法,它并没有那么难看,实际上当他们没有 ui 事件时延迟是 10ms,但是一旦他们是事件,延迟就会下降到 0 毫秒,应用程序等待 1000 毫秒,然后再回到 10 毫秒(我想不要使用所有的电池)。接缝逻辑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多