Перейти к содержанию
Fire Monkey от А до Я
  • 0

Таймер в сервисе


Rusland

Вопрос

Обычный TTimer использовать нельзя. Кстати почему нельзя? Почему он обращается к Активити в Androidapi.Helpers?

Embarcadero исправьте уже свой баг.

 

Как реализовать timer в сервисе?

Изменено пользователем Rusland
Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Когда-то, лет 5 назад у меня возникла такая-же проблема, только в Win32-сервисе. Тогда, для её решения, я написал 2 простеньких класса.

Вот код, может пригодится?

unit Utils.TimerThread;

interface

uses
  System.Classes, System.SysUtils, System.SyncObjs;


type
  TCustomTimerThread = class abstract (TThread)
  private
    FLock: TCriticalSection;
    FCancelledEvent: TSimpleEvent;

    FInterval: Integer;
    FOnTimer: TNotifyEvent;

    function GetInterval: Integer;
    function GetOnTimer: TNotifyEvent;
    procedure SetInterval(const Value: Integer);
    procedure SetOnTimer(const Value: TNotifyEvent);
  protected
    procedure Lock;
    procedure Unlock;

    procedure Sleep(AInterval: Integer); reintroduce;
    procedure TerminatedSet; override;

    procedure DoOnTimer; virtual;
  public
    constructor Create(AInterval: Integer; AOnTimer: TNotifyEvent); reintroduce;
    procedure BeforeDestruction; override;
    procedure Cancel; virtual;

    property Interval: Integer read GetInterval write SetInterval;
    ///	<summary>
    ///   <para>
    ///	    За синхронизацией потоков отвечает поток в котором обрабатывается OnTimer
    ///   </para>
    ///   <para>
    ///	    !!! НЕ ЗАБЫВАТЬ ПРО ЭТО !!!
    ///   </para>
    ///	</summary>
    property OnTimer: TNotifyEvent read GetOnTimer write SetOnTimer;
  end;

  ///	<summary>
  ///	  Simple wait thread
  ///	</summary>
  ///	<remarks>
  ///	  <para>
  ///	    !!! Important !!!
  ///	  </para>
  ///	  <para>
  ///	    Use Cancel instead of Terminate. You can get ThreadExternalTerminate
  ///	    exception in multi-thread applications
  ///	  </para>
  ///	</remarks>
  TWaitThread = class(TCustomTimerThread)
  protected
    procedure Execute; override;
  public
  end;

  ///	<summary>
  ///	  Thread independed timer
  ///	</summary>
  ///	<remarks>
  ///	  <para>
  ///	    !!! Important !!!
  ///	  </para>
  ///	  <para>
  ///	    Use Cancel instead of Terminate. You can get ThreadExternalTerminate
  ///	    exception in multi-thread applications
  ///	  </para>
  ///	</remarks>
  TTimerThread = class(TCustomTimerThread)
  private
    FEnabled: Boolean;

    function GetEnabled: Boolean;
    procedure SetEnabled(const Value: Boolean);
  protected
    procedure Execute; override;
  public
    constructor Create(AInterval: Integer; AOnTimer: TNotifyEvent; AEnabled: Boolean = True); reintroduce;
    property Enabled: Boolean read GetEnabled write SetEnabled;

  end;

implementation

{ TCustomTimerThread }

procedure TCustomTimerThread.BeforeDestruction;
begin
  FLock.Free;
  FreeAndNil(FCancelledEvent);

  inherited;
end;

constructor TCustomTimerThread.Create(AInterval: Integer; AOnTimer: TNotifyEvent);
begin
  inherited Create;

  FInterval := AInterval;
  FOnTimer := AOnTimer;

  FreeOnTerminate := True;

  FLock := TCriticalSection.Create;
  FCancelledEvent := TSimpleEvent.Create;
  FCancelledEvent.ResetEvent;
end;

procedure TCustomTimerThread.Cancel;
begin
  FCancelledEvent.SetEvent;
end;

procedure TCustomTimerThread.DoOnTimer;
begin
  if Assigned(OnTimer) then
    OnTimer(Self);
end;

function TCustomTimerThread.GetInterval: Integer;
begin
  Lock;
  try
    Result := FInterval;
  finally
    Unlock;
  end;
end;

function TCustomTimerThread.GetOnTimer: TNotifyEvent;
begin
  Lock;
  try
    Result := FOnTimer;
  finally
    Unlock;
  end;
end;

procedure TCustomTimerThread.Lock;
begin
  FLock.Enter;
end;

procedure TCustomTimerThread.SetInterval(const Value: Integer);
begin
  Lock;
  try
    FInterval := Value;
  finally
    Unlock;
  end;
end;

procedure TCustomTimerThread.SetOnTimer(const Value: TNotifyEvent);
begin
  Lock;
  try
    FOnTimer := Value;
  finally
    Unlock;
  end;
end;

procedure TCustomTimerThread.Sleep(AInterval: Integer);
begin
  FCancelledEvent.WaitFor(AInterval);
end;

procedure TCustomTimerThread.TerminatedSet;
begin
  inherited;

  FCancelledEvent.SetEvent;
end;

procedure TCustomTimerThread.Unlock;
begin
  FLock.Leave;
end;

{ TWaitThread }

procedure TWaitThread.Execute;
begin
  if FCancelledEvent.WaitFor(FInterval) = wrTimeout then
    DoOnTimer;
end;

{ TTimerThread }

constructor TTimerThread.Create(AInterval: Integer; AOnTimer: TNotifyEvent; AEnabled: Boolean);
begin
  inherited Create(AInterval, AOnTimer);

  FOnTimer := AOnTimer;
  FEnabled := AEnabled;
end;

procedure TTimerThread.Execute;
begin
  while not Terminated do
    case FCancelledEvent.WaitFor(FInterval) of
      wrTimeout:
        begin
          if Enabled then
            DoOnTimer;
        end;
      else
        Break;
    end;
end;

function TTimerThread.GetEnabled: Boolean;
begin
  Lock;
  try
    Result := FEnabled;
  finally
    Unlock;
  end;
end;

procedure TTimerThread.SetEnabled(const Value: Boolean);
begin
  Lock;
  try
    FEnabled := Value;
  finally
    Unlock;
  end;
end;

end.

 

Изменено пользователем dnekrasov
Исправлен исходник
Ссылка на комментарий
  • 0
10 часов назад, dnekrasov сказал:

Вот код, может пригодится?

Спасибо большое, хороший полезный класс!

Можно еще выложить простой пример использования — создание, инициализация, запуск, остановка, уничтожение? Думаю, очень поможет форумчанам.

 

Ссылка на комментарий
  • 0

В архиве - простенький проект. Протестирован на Win и OSX.

 

TimerThreadDemo.zip

Изменено пользователем dnekrasov
Заменен архив - отсутствовал один файл
Ссылка на комментарий
  • 0

dnekrasov, не удалось понять, как правильно запустить таймер в сервисе.

Ваш модуль хорошо работает в обычном приложении, но в сервисе не срабатывает событие таймера. Ни WaitComplete, ни TimerThreadOnTimer. Кстати в приведенном Вами примере нет формы, только модуль.

Приведу код моего сервиса, и лог, может кто-нибудь поймет в чем дело. Сервис нормально работает и вручную (по событию остановки сервиса) нормально выключается.

unit Unit1;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Android.Service,
  AndroidApi.JNI.GraphicsContentViewText,
  AndroidApi.JNI.Os,
  System.IOUtils,
  Utils.TimerThread,
  AndroidApi.JNI.App, // TJService.JavaClass.START_STICKY
  AndroidApi.Log,
  AndroidApi.JNI.JavaTypes, // JString
  AndroidApi.Helpers; // StringToJString

const
{$IF DEFINED(ANDROID) OR DEFINED(IOS)}
  AUDIO_FILENAME = 'test.caf';
{$ELSE}
  AUDIO_FILENAME = 'test.wav';
{$ENDIF}

Const
  RecordTime = 3000;

type
  TDM = class(TAndroidService)
    function AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent;
      Flags, StartId: Integer): Integer;
    function AndroidServiceHandleMessage(const Sender: TObject; const AMessage: JMessage): Boolean;
    function AndroidServiceBind(const Sender: TObject; const AnIntent: JIntent): JIBinder;
    procedure AndroidServiceTaskRemoved(const Sender: TObject; const ARootIntent: JIntent);
  private
    FTimer: TTimerThread;

    procedure WaitComplete(Sender: TObject);
    procedure TimerThreadStart;
    procedure TimerThreadStop;
    procedure TimerThreadOnTimer(Sender: TObject);
  public
    { Public declarations }
  end;

var
  DM: TDM;

implementation

{ %CLASSGROUP 'FMX.Controls.TControl' }

{$R *.dfm}

procedure Log(const Fmt: string; const Params: array of const);
var
  Msg: string;
  M: TMarshaller;
begin
  Msg := Format(Fmt, Params);
  LOGI(M.AsUtf8(Msg).ToPointer);
end;

function TDM.AndroidServiceBind(const Sender: TObject; const AnIntent: JIntent): JIBinder;
begin
  Log('+ BIND: ' + JStringToString(AnIntent.getAction.toString) + ' - ' +
    JStringToString(AnIntent.getData.toString), []);
end;

function TDM.AndroidServiceHandleMessage(const Sender: TObject; const AMessage: JMessage): Boolean;
begin
  Log('+ MESSAGE: ' + JStringToString(AMessage.toString) + ' - ' +
    JStringToString(AMessage.getData.toString), []);
  Result := True;
end;

function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent;
  Flags, StartId: Integer): Integer;
begin
  Log('+ START: ' + JStringToString(Intent.getAction.toString), []);
  if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then
  begin
    Log('...Service stoped', []);
    TimerThreadStop;
    JavaService.stopSelf;
    Result := TJService.JavaClass.START_NOT_STICKY;
  end
  else
  begin
    Log('...Service started', []);
    TimerThreadStart;
    Result := TJService.JavaClass.START_STICKY;
  end;
end;

procedure TDM.AndroidServiceTaskRemoved(const Sender: TObject; const ARootIntent: JIntent);
begin
  Log('- TASK REM: ' + JStringToString(ARootIntent.getAction.toString), []);
end;

procedure TDM.TimerThreadOnTimer(Sender: TObject);
begin
  TThread.Synchronize(TThread(Sender),
    procedure
    begin
      Log('+++ Timer worked.', []);
      TimerThreadStop;
    end);
end;

procedure TDM.TimerThreadStart;
begin
  if not Assigned(FTimer) then
    FTimer := TTimerThread.Create(RecordTime, TimerThreadOnTimer)
  else
    FTimer.Interval := RecordTime;

  FTimer.Enabled := True;

  Log('+ Timer started', []);
end;

procedure TDM.TimerThreadStop;
begin
  if not Assigned(FTimer) or not FTimer.Enabled then
  begin
    Log('- Timer not enabled', []);
    Exit
  end
  else
  begin
    FTimer.Enabled := False;
    FTimer.Cancel;
  end;
  Log('- Timer stoped', []);
end;

procedure TDM.WaitComplete(Sender: TObject);
begin
  TThread.Synchronize(TThread(Sender),
    procedure
    var
      Msg: string;
      M: TMarshaller;
    begin
      Log('- WaitComplete', []);

      JavaService.stopSelf;

      Log('Service stoped', []);
    end);
end;

end.

«FMX: Project1:»  — это мой хост, который запускает сервис. Остальное выдает сервис.

Что я сделал? — Запустил хост на HTC One, отправил Intent «StartIntent», сервис запустился, поток запустился. Подождал несколько секунд, отправил Intent «StopIntent», поток остановился, сервис остановился.

Далее лог: 

05-31 23:38:29.709: I/info(17885): FMX: Project1: Try to start
05-31 23:38:29.712: W/linker(17885): /data/app/com.embarcadero.Project1-1/lib/arm/libProject2.so: is missing DT_SONAME will use basename as a replacement: "libProject2.so"
05-31 23:38:29.985: I/info(17885): + START: StartIntent
05-31 23:38:29.985: I/info(17885): ...Service started
05-31 23:38:29.985: I/info(17885): + Timer started
05-31 23:38:41.754: I/info(17885): FMX: Project1: Try to stop
05-31 23:38:41.755: I/info(17885): + START: StopIntent
05-31 23:38:41.755: I/info(17885): ...Service stoped
05-31 23:38:41.755: I/info(17885): - Timer stoped

 

Изменено пользователем Pax Beach
Ссылка на комментарий
  • 0
  • Модераторы

Старт таймера с 0 интервалом происходит?

if not Assigned(FTimer) then
  FTimer := TTimerThread.Create(RecordTime, TimerThreadOnTimer);
FTimer.Interval := RecordTime;

UPDATE: не заметил, что в конструкторе интервал как параметр

Изменено пользователем ZuBy
Ссылка на комментарий
  • 0

Насчет андроидных сервисов - ничего не могу сказать (просто никогда ими не интересовался), а в виндовом сервисе все работает без проблем (для него и писалось).

Могу лишь посоветовать не использовать TThread.Synchronize, ведь этот метод синхронизирует с главным потоком приложения, а сервисы работают в отдельном потоке, а в главном происходит только обработка сообщений сервису.

Ссылка на комментарий
  • 0
  • Администраторы

Посмотрите на реализацию системных таймеров в Androidapi.Timer.pas. Они подходят для сервисов.

Ссылка на комментарий
  • 0
10 часов назад, Brovin Yaroslav сказал:

Посмотрите на реализацию системных таймеров в Androidapi.Timer.pas. Они подходят для сервисов.

Спасибо за подсказку.

Что-то я не пойму, как с этим модулем правильно работать.

Таймер нужно запускать через TTimerManager из FMX.Platform.Android.pas или AndroidTimerCreate->AndroidTimerSetInterval->AndroidTimerSetHandler?

Примеров и документации нигде нет. Опять на 4 часа завис с кодом =(

 

Ссылка на комментарий
  • 0
  • Администраторы
function AndroidTimerCreate: Integer;

Создает таймер и возвращает его идентификатор

procedure AndroidTimerSetInterval(TimerHandle: Integer; Interval: Integer);

Задает для созданного таймера по его ID интервал срабатывания.

Эта реализация работает с одним обработчиком OnTimer для всех таймеров. Чтобы задать такой общий обработчик используется процедура:

procedure AndroidTimerSetHandler(OnTimer: TAndroidTimerNotify);

В параметре OnTimer приходит ваш идентификатор таймера.

procedure AndroidTimerDestroy(TimerHandle: Integer);

Этот метод уничтожает созданный вами таймер по ID

Этот юнит - это оболочка над posix-таймерами

Ссылка на комментарий
  • 0

Brovin Yaroslav, заработал мой сервис с этим модулем, спасибо!

Но... при уничтожении таймера умирает сервис и хост, вылетают с ошибкой «Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?)» (ниже в логе).

Видимо нужно для таймера сначала нужно как-то выполнить DetachCurrentThread. Не понимаю только — Как?

Коллеги, посоветуете, что можно сделать с отключением таймера?

Так я запускаю таймер:

procedure TDM.StartTimer;
begin
  Log('... timer to be started', []);
  FTimerHandle := AndroidTimerCreate;
  FTimerCounter := 0;
  AndroidTimerSetInterval(FTimerHandle, TimerInterval);

  AndroidTimerSetHandler(WaitComplete);
  Log('+ Timer started', []);
end;

Так я останавливаю таймер. После выполнения таймера, сервис мне не нужен тоже, останавливаю.

procedure TDM.StopTimer;
begin
  StopRecord;

  if FTimerHandle > 0 then
  begin
    Log('... timer to be stoped', []);
    AndroidTimerSetHandler(nil);
    Log('... timer to be destroyed', []);
    AndroidTimerDestroy(FTimerHandle); // ТУТ УМИРАЕТ СЕРВИС И ХОСТ-ПРИЛОЖЕНИЕ !!!
    FTimerHandle := 0;
    Log('- Timer stoped', []);
  end;

  Log('... Service to be stoped', []);
  JavaService.stopSelf;
end;

Вот так я работаю с запуском и остановкой сервиса:

function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent;
  Flags, StartId: Integer): Integer;
begin
  Log('+ START with Intent: ' + JStringToString(Intent.getAction.toString), []);
  if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then
  begin
    StopRecord;

    StopTimer;

    Log('...Service to be stoped', []);
    JavaService.stopSelf;

    Result := TJService.JavaClass.START_NOT_STICKY; // don't reload service
  end
  else
  begin
    Log('... service started', []);

    if not IsMicrophoneRecording then
    begin
      Log('... sound record to be started', []);
      StartRecord;
    end;

    StartTimer;

    Result := TJService.JavaClass.START_STICKY; // rerun service if it stops
  end;
end;

 

А вот лог, где последние две строки — ошибка, при попытке уничтожить таймер:

06-02 16:57:50.460: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libProjectAppRec.so: is missing DT_SONAME will use basename as a replacement: "libProjectAppRec.so"
06-02 16:57:51.679: I/Adreno-EGL(17598): <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: Nondeterministic_AU_msm8974_LA.BF.1.1.3__release_AU (I3fa967cfef)
06-02 16:57:51.679: I/Adreno-EGL(17598): OpenGL ES Shader Compiler Version: E031.28.00.02
06-02 16:57:51.679: I/Adreno-EGL(17598): Build Date: 09/29/15 Tue
06-02 16:57:51.679: I/Adreno-EGL(17598): Local Branch: mybranch14683032
06-02 16:57:51.679: I/Adreno-EGL(17598): Remote Branch: quic/master
06-02 16:57:51.679: I/Adreno-EGL(17598): Local Patches: NONE
06-02 16:57:51.679: I/Adreno-EGL(17598): Reconstruct Branch: NOTHING
06-02 16:58:32.859: I/info(17598): FMX: ProjectAppRec: Try to start
06-02 16:58:32.880: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libProxyAndroidService.so: is missing DT_SONAME will use basename as a replacement: "libProxyAndroidService.so"
06-02 16:58:32.905: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libSvcTimer.so: is missing DT_SONAME will use basename as a replacement: "libSvcTimer.so"
06-02 16:58:33.237: I/info(17598): + START with Intent: StartIntent
06-02 16:58:33.237: I/info(17598): ... service started
06-02 16:58:33.237: I/info(17598): ... timer to be started
06-02 16:58:33.237: I/info(17598): + Timer started
06-02 16:58:34.240: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:35.238: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:36.238: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:37.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:38.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:39.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:40.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:41.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:42.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:43.239: I/info(17598): +++ Timer is triggered  time.
06-02 16:58:44.237: I/info(17598): ... timer to be stoped
06-02 16:58:44.237: I/info(17598): ... timer to be destroyed
06-02 16:58:44.237: I/info(17598): - Timer stoped
06-02 16:58:44.237: I/info(17598): ... Service to be stoped
06-02 16:58:44.239: W/art(17598): Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?): Thread[12,tid=17927,Native,Thread*=0xba7bbb50,peer=0x12d34100,"Thread-42654"]
06-02 16:58:44.239: A/art(17598): art/runtime/thread.cc:1228] Native thread exited without calling DetachCurrentThread: Thread[12,tid=17927,Native,Thread*=0xba7bbb50,peer=0x12d34100,"Thread-42654"]

 

Изменено пользователем Pax Beach
внесены уточнения вопроса
Ссылка на комментарий
  • 0

Это не хорошее решение, а костыль, но пока решает мою задачу:

Когда нужное мне событие произошло, я просто убираю обработчик таймера

AndroidTimerSetHandler(nil);

А когда нужен таймер, снова назначаю обработчик:

AndroidTimerSetHandler(WaitComplete);

При этом таймер продолжает работать и занимать процесс, когда он фактически не нужен — это, конечно же, не хорошо.

 

Ссылка на комментарий
  • 0

А можно поподробнее! Необходимо что бы через каждые 10 сек выполнялась функция "Функция"

Создаю таймер

TimerID :=  AndroidTimerCreate;
AndroidTimerSetInterval(TimerID,10000);

Назначаю обработчик  вылетает ошибка
 

  AndroidTimerSetHandler("Функция");

как правильно назначить обработчик на нужную функцию

Изменено пользователем cherezovmax
Ссылка на комментарий
  • 0
1 час назад, cherezovmax сказал:

А можно поподробнее! Необходимо что бы через каждые 10 сек выполнялась функция "Функция"

Создаю таймер


TimerID :=  AndroidTimerCreate;
AndroidTimerSetInterval(TimerID,10000);

Назначаю обработчик  вылетает ошибка
 


  AndroidTimerSetHandler("Функция");

как правильно назначить обработчик на нужную функцию

Вот пример, в котором реализованы ваши задачи.

Ссылка на комментарий
  • 0

cherezovmax

uses
  AndroidApi.Log,            // LOGI
  Androidapi.Timer,
...
  private
    { Private declarations }
    FTimerHandle: integer;
    FTimerCounter: integer;
    TimerInterval: integer;
    procedure StartTimer;
    procedure WaitComplete(TimerId: Integer);

...
procedure TDM.AndroidServiceCreate(Sender: TObject);
begin
  FTimerHandle := AndroidTimerCreate;
  FTimerCounter := 0;
  TimerInterval:=5000;
end;

function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  StartTimer;
  LogI('TJService.JavaClass.START_STICKY');
  Result := TJService.JavaClass.START_STICKY;
end;

procedure TDM.StartTimer;
begin
  LogI('... timer to be started');
  AndroidTimerSetInterval(FTimerHandle, TimerInterval);
  AndroidTimerSetHandler(WaitComplete);
  LogI('+ Timer started');
end;


procedure TDM.WaitComplete(TimerId: Integer);
begin
  LogI('WaitComplete procedure')
end;

 

Изменено пользователем Rusland
Ссылка на комментарий
  • 0

Подскажите, куда здесь влепить свою функцию?) 

В 21.09.2016 в 12:46, Rusland сказал:

procedure TDM.WaitComplete(TimerId: Integer);
begin
  LogI('WaitComplete procedure')
end;

В WaitComplete правильно понимаю? И как можно из своей функции стартовать таймер и отключать его при прохождении интервала? :/

Ссылка на комментарий
  • 0
1 час назад, MikeWuzHere сказал:

Подскажите, куда здесь влепить свою функцию?) 

В WaitComplete правильно понимаю? И как можно из своей функции стартовать таймер и отключать его при прохождении интервала? :/

Так есть же пример выше

Ссылка на комментарий
  • 0

имхо, я бы на старте службы запустил цикл со Sleep(10) и проверял сколько прошло времени вот этим кодом:

class MyTimer
{
private:
	clock_t startTicks;
	double coef;
public:
	MyTimer(){ coef = 1000.0f / CLOCKS_PER_SEC; }
	void Start() { startTicks = clock(); }
	float GetTimeSec(){ return ( clock() - startTicks ) * coef / 1000.0f; }
	float GetTimeMSec(){ return ( clock() - startTicks ) * coef; }
};

работает на винде и андроид, что-то типа:

MyTimer Timer;
Timer.Start();
while(true)
	{
		if ( flag_exit ) break;
		if ( Timer.GetTimeSec() < 15 )
        	{
              // что-то делаем в режиме ожидания
              Sleep(10);
            }
		// что-то делаем по таймеру	
        Timer.Start();                      
	}

код на си, но все функции есть в объект-паскале, думаю алгоритм понятен

хотя не факт что #include <time.h> есть в дельфи, а в билдере нет служб андроид, хаха

Изменено пользователем Камышев Александр
Ссылка на комментарий
  • 0

нету clock() в Delphi, сишный эксклюзив, только GetTickCount в TThread...

с другой стороны зачем такая точность, clock() возвращает тики процессора, тут достаточно системное время запросить

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...