为什么 Android 上的计时器在手指触摸屏幕时更准确?
Why is a Timer on Android more accurate when a finger is touching the screen?
在 Delphi 10.1 柏林,我正在制作一个 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;
然后一切正常,事件每 2 毫秒触发一次(没有 TThread.synchronize 每 1 毫秒触发一次),并且这个手指是否在屏幕上。
与 VCL 的 TTimer
不同,FMX 在 Android 上的 TTimer
效率很低。
当计时器间隔结束时,Android 使用回调函数通知 FMX(在 Androidapi.Timer
单元中)。该回调由工作线程调用,并将计时器推入线程安全队列(在 FMX.Platform.Android
单元中)。
当主 UI 线程定期检查未决 UI 消息时,它还会检查计时器队列,如果有任何计时器排队,则调用它们的 OnTimer
事件处理程序(按照他们排队的顺序)。
但是,如果没有待处理的 UI 消息,FMX 可能会延迟检查计时器队列!然后,一旦处理了所有 UI 消息,再次处理事件之前可能会有延迟。这完全取决于 FMX 应用程序的内部状态。
因此,无法保证 1 毫秒计时器会在接近 1 毫秒间隔的任何地方触发其 OnTimer
事件处理程序。它也可以解释为什么 UI activity 的增加可以让 OnTimer
事件更频繁地触发,因为 UI 消息被更频繁地处理。
这与VCL的TTimer
不同,它基于WM_TIMER
UI消息,是低优先级的消息,只有在没有其他UI 待处理的消息。当它生成时,它会直接被调度到 TTimer
的内部 window,没有额外的排队来增加额外的开销层。但是,增加 UI activity 会减慢 TTImer.OnTimer
事件的触发速度,而不是加快它的速度。
在 TThread.Synchronize()
的情况下,它会在需要处理挂起的同步请求时主动通知主 UI 线程,从而允许主 UI 线程检查和尽早执行同步程序。
在 Delphi 10.1 柏林,我正在制作一个 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;
然后一切正常,事件每 2 毫秒触发一次(没有 TThread.synchronize 每 1 毫秒触发一次),并且这个手指是否在屏幕上。
与 VCL 的 TTimer
不同,FMX 在 Android 上的 TTimer
效率很低。
当计时器间隔结束时,Android 使用回调函数通知 FMX(在 Androidapi.Timer
单元中)。该回调由工作线程调用,并将计时器推入线程安全队列(在 FMX.Platform.Android
单元中)。
当主 UI 线程定期检查未决 UI 消息时,它还会检查计时器队列,如果有任何计时器排队,则调用它们的 OnTimer
事件处理程序(按照他们排队的顺序)。
但是,如果没有待处理的 UI 消息,FMX 可能会延迟检查计时器队列!然后,一旦处理了所有 UI 消息,再次处理事件之前可能会有延迟。这完全取决于 FMX 应用程序的内部状态。
因此,无法保证 1 毫秒计时器会在接近 1 毫秒间隔的任何地方触发其 OnTimer
事件处理程序。它也可以解释为什么 UI activity 的增加可以让 OnTimer
事件更频繁地触发,因为 UI 消息被更频繁地处理。
这与VCL的TTimer
不同,它基于WM_TIMER
UI消息,是低优先级的消息,只有在没有其他UI 待处理的消息。当它生成时,它会直接被调度到 TTimer
的内部 window,没有额外的排队来增加额外的开销层。但是,增加 UI activity 会减慢 TTImer.OnTimer
事件的触发速度,而不是加快它的速度。
在 TThread.Synchronize()
的情况下,它会在需要处理挂起的同步请求时主动通知主 UI 线程,从而允许主 UI 线程检查和尽早执行同步程序。