【问题标题】:How to create a timer in a lib for mobile app using Delphi XE5?如何在使用 Delphi XE5 的移动应用程序库中创建计时器?
【发布时间】:2014-07-18 07:24:19
【问题描述】:

我试图弄清楚如何在库中为使用 Delphi XE5 的移动应用程序实现计时器。此处显示的代码Delphi - timer inside thread generates AV 无法使用,因为我不知道我的库的实例化对象的任何引用以使用同步。

感谢任何帮助

【问题讨论】:

    标签: delphi mobile timer delphi-xe5


    【解决方案1】:

    这是一个线程计时器,您可以将其用作原始计时器组件 - 但它不是一个组件

    uses
      ...
      ThreadTimer;
    
    type
      TForm1 = class( TForm )
      private
        FTimer : TTimerThread;
        procedure TimerOnTimer( Sender : TObject );
      public
        procedure AfterConstruction; override;
        procedure BeforeDestruction; override;
      end;
    
    procedure TForm1.AfterConstruction;
    begin
      inherited;
      FTimer := TTimerThread.Create;
      FTimer.OnTimer := TimerOnTimer;
      // FTimer.Interval := 5000;
      // FTimer.Enabled := False;
    end;
    
    procedure TForm1.BeforeDestruction;
    begin
      inherited;
      FTimer.Free;
    end;
    
    procedure TForm1.TimerOnTimer( Sender : TObject );
    begin
      // Do whatever you like in here
    
    end;
    

    这里是完整的单元

    unit ThreadTimer;
    
    interface
    
    uses
      System.Classes,
      System.SyncObjs;
    
    type
      TTimerThread = class( TThread )
    {$REGION 'Constants'}
      private const
        C_DEFAULT_INTERVAL = 1000;
        C_DEFAULT_ENABLED = True;
    {$ENDREGION}
    {$REGION 'Syncronization'}
      private
        FCS : TCriticalSection;
        FEvent : TEvent;
    {$ENDREGION}
      private
        FInterval : Cardinal;
        FOnTimer : TNotifyEvent;
        FEnabled : Boolean;
        function GetInterval : Cardinal;
        procedure SetInterval( const Value : Cardinal );
        function GetOnTimer : TNotifyEvent;
        procedure SetOnTimer( const Value : TNotifyEvent );
        function GetEnabled : Boolean;
        procedure SetEnabled( const Value : Boolean );
      protected
        procedure TerminatedSet; override;
        procedure Execute; override;
      public
        constructor Create;
        destructor Destroy; override;
    
        property Interval : Cardinal read GetInterval write SetInterval default C_DEFAULT_INTERVAL;
        property OnTimer : TNotifyEvent read GetOnTimer write SetOnTimer;
        property Enabled : Boolean read GetEnabled write SetEnabled default C_DEFAULT_ENABLED;
      end;
    
    implementation
    
    { TTimerThread }
    
    constructor TTimerThread.Create;
    begin
      inherited Create( False );
      FCS := TCriticalSection.Create;
      FEvent := TEvent.Create( nil, False, False, '' );
      FInterval := C_DEFAULT_INTERVAL;
      FEnabled := C_DEFAULT_ENABLED;
    end;
    
    destructor TTimerThread.Destroy;
    begin
    
      inherited;
      FEvent.Free;
      FCS.Free;
    end;
    
    procedure TTimerThread.Execute;
    var
      LInterval : Cardinal;
    begin
      inherited;
      while not Terminated do
        begin
    
          FCS.Enter;
          try
            if FEnabled
            then
              LInterval := FInterval
            else
              LInterval := INFINITE;
          finally
            FCS.Leave;
          end;
    
          if ( FEvent.WaitFor( LInterval ) = wrTimeout )
          then
            Synchronize(
                procedure
              begin
                if Assigned( OnTimer )
                then
                  OnTimer( Self );
              end );
        end;
    end;
    
    function TTimerThread.GetEnabled : Boolean;
    begin
      FCS.Enter;
      try
        Result := FEnabled;
      finally
        FCS.Leave;
      end;
    end;
    
    function TTimerThread.GetInterval : Cardinal;
    begin
      FCS.Enter;
      try
        Result := FInterval;
      finally
        FCS.Leave;
      end;
    end;
    
    function TTimerThread.GetOnTimer : TNotifyEvent;
    begin
      FCS.Enter;
      try
        Result := FOnTimer;
      finally
        FCS.Leave;
      end;
    end;
    
    procedure TTimerThread.SetEnabled( const Value : Boolean );
    begin
      FCS.Enter;
      try
        if FEnabled <> Value
        then
          begin
            FEnabled := Value;
            FEvent.SetEvent;
          end;
      finally
        FCS.Leave;
      end;
    end;
    
    procedure TTimerThread.SetInterval( const Value : Cardinal );
    begin
      FCS.Enter;
      try
        if FInterval <> Value 
        then
          begin
            FInterval := Value;
            FEvent.SetEvent;
          end;
      finally
        FCS.Leave;
      end;
    end;
    
    procedure TTimerThread.SetOnTimer( const Value : TNotifyEvent );
    begin
      FCS.Enter;
      try
        FOnTimer := Value;
      finally
        FCS.Leave;
      end;
    end;
    
    procedure TTimerThread.TerminatedSet;
    begin
      inherited;
      FEvent.SetEvent;
    end;
    
    end.
    

    【讨论】:

    • @DavidHeffernan FEvent.Free; FCS.Free; 必须继承之后。否则,如果线程正在运行并且您调用 Free 继承的析构函数调用 Terminate 并调用 TerminatedSet 并触发事件 - uups,哪个事件......你已经释放它......也在Execute仍在等待事件触发而不是被释放。请通过之前之后inherited自行检查本单元
    • 那么在我看来,您以错误的方式解决了问题。您还恢复了 TForm1.BeforeDestruction 更改。我无法为带有此类陷阱的代码投票。
    • @DavidHeffernan 您可以修复我的代码,但请不要破坏它。在我看来,这是释放关键部分和事件的正确方法和正确时间(在inherited 之后)。在 this 的情况下,FTimer 是在 inherited 之前还是之后释放都没有关系。
    • 我很抱歉打破它。那里没有争论。在非标准位置继承两次的事实是代码味道。
    • @DavidHeffernan 不是,这是必须的。否则,您不能简单地调用Free,因为您必须在调用Free 之前调用Terminate,这将是代码异味。别人怎么会知道这个事实。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    相关资源
    最近更新 更多