如何在 TThread 中启用 AniIndicator1,而不是在 FMX 的主进程中?

How to enable an AniIndicator1 in a TThread , not in the main process in FMX?

嗨,我有一个基于 FMX 的多设备应用程序。

同步数据库过程需要更长的时间,我需要启用一个 AniIndicator1 来告诉用户等待。

以下代码在n android phone中测试过,有时有效,有时只完成第一个同步DB功能,有时先完成3个然后退出。 sychDB函数有时none发生

procedure TForm1.BtnSyncDBClick(Sender: TObject);
begin 
  Application.ProcessMessages;
  memo3.Lines.Add('Start synchronising...');
  AniIndicator1.Visible:=true;
  AniIndicator1.Enabled:=true;
      TThread.CreateAnonymousThread(
    procedure
    begin
      try
        SynchDB_A;
          memo3.Lines.Add('finish 0');
         SynchDB_B;
         memo3.Lines.Add('finish 1');
         SynchDB_C ;
          memo3.Lines.Add('finish 2');
         SynchDB_D;
          memo3.Lines.Add('finish 3');
      finally
        AniIndicator1.Enabled := False;
        AniIndicator1.Visible := False;
      end;
    end
    ).start;
end;

所以,我想知道是否可以将AniIndicator1.enable函数放在子线程中,并使用主线程来同步数据库?

尝试了以下代码,但没有按预期工作。

procedure TForm1.BtnSyncDBClick(Sender: TObject);
    begin 
           TThread.CreateAnonymousThread(
    procedure
    begin
      try
         TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
           Memo1.lines.Add('Start... ');
            AniIndicator1.Visible := True;
            AniIndicator1.Enabled := True;
          end);    
      finally

      end;
    end
  ).Start;

          try
            SynchDB_A;
              memo3.Lines.Add('finish 0');
             SynchDB_B;
             memo3.Lines.Add('finish 1');
             SynchDB_C ;
              memo3.Lines.Add('finish 2');
             SynchDB_D;
              memo3.Lines.Add('finish 3');
          finally
            AniIndicator1.Enabled := False;
            AniIndicator1.Visible := False;
          end;
end;

任何专家都可以对此提供帮助,要么修复第一个代码以保证始终有效,子线程不会被杀死,要么在主进程中启动 SynchDB 并在子线程中启用 AniIndicator1 并使其旋转。或者任何其他告诉用户在 synchDB 工作时等待的简单方法?

提前致谢。

您不能像您正在做的那样从工作线程内直接访问 UI 控件。您必须同步对主 UI 线程的访问。

procedure TForm1.BtnSyncDBClick(Sender: TObject);
var
  Thread: TThread;
begin 
  Application.ProcessMessages;
  memo3.Lines.Add('Start synchronising...');
  AniIndicator1.Visible := True;
  AniIndicator1.Enabled := True;
  Thread := TThread.CreateAnonymousThread(
    procedure
    begin
      SynchDB_A; // <-- must do thread-safe work!
      TThread.Queue(nil,
        procedure
        begin
          memo3.Lines.Add('finish 0');
        end
      );
      SynchDB_B; // <-- must do thread-safe work!
      TThread.Queue(nil,
        procedure
        begin
          memo3.Lines.Add('finish 1');
        end
      );
      SynchDB_C; // <-- must do thread-safe work!
      TThread.Queue(nil,
        procedure
        begin
          memo3.Lines.Add('finish 2');
        end
      );
      SynchDB_D; // <-- must do thread-safe work!
      TThread.Queue(nil,
        procedure
        begin
          memo3.Lines.Add('finish 3');
        end
      );
    end
  );
  Thread.OnTerminate := SyncDone;
  Thread.Start;
end;

procedure TForm1.SyncDone(Sender: TObject);
begin
  AniIndicator1.Enabled := False;
  AniIndicator1.Visible := False;
  if TThread(Sender).FatalException <> nil then
    memo3.Lines.Add('Error: ' + Exception(TThread(Sender).FatalException).Message);
end;

您的第二个代码不起作用的原因是主 UI 线程正在阻止执行所有工作,因此在该工作完成之前它无法更新 UI。因此需要在线程中进行非 UI 工作。

为什么要在 onclick 事件中调用 application.processmessages? 您已经在主线程消息循环中...

你也可以使用TTask.run,它比TThread.CreateAnonymousThread

更简单
TTask.run(
procedure
var
  msg : string;
begin
  try
   SynchDB_A;
   msg:='finish 1';
  except
    on e:exception do begin
      msg:=e.message;
    end;
  end;
  TThread.Queue(Nil,
  procedure
  begin
    memo3.lines.add(msg);
  end);

试试这个,创建这个程序

procedure TFDashboard.fnLoading(isEnabled : Boolean);
begin
  TThread.Synchronize(nil, procedure begin
    AniIndicator1.Visible := isEnabled;
    AniIndicator1.Enabled := isEnabled;
  end);
end;

之后,您可以在 TTask 中调用该过程

procedure TFDashboard.FirstShow;
begin
  TTask.Run(procedure begin
    fnLoading(True);
    try

    finally
      fnLoading(False);
    end;
  end).Start;
end;