Android Tablet上的线程导致冻结

时间:2018-11-02 13:11:49

标签: multithreading delphi firemonkey

我正在Delphi Tokyo中开发Firemonkey应用程序,并决定添加一个执行某些动画的加载器屏幕。我有一个带有列表动画的表单,该表单在一个线程中运行,然后我对datasnap服务器的调用在另一个线程中运行。我这样做是因为如果两个调用都不在线程中,则无法使动画正常工作。

现在在Windows版本上运行此程序可以正常工作。在我的华为手机和另一台三星平板电脑上运行它都可以在70%的时间内运行。其他30%的时间冻结,我必须终止该应用程序。完成数据快照加载后,应该释放并关闭加载程序窗体,并且将主面板的不透明度设置为1,然后再次启用该面板。我不确定100%是否冻结了应用程序以及代码是否未成功运行,那就应该再次启用面板。我能够在不工作的情况下进行一次调试,从而产生内存不足错误,但是在通过电话进行调试时无法重新创建问题。

这个想法是,当按下记录按钮时,加载程序屏幕会在检索数据时显示一些动画,然后再次将其隐藏。我在下面的代码中做错了吗?

  ShowLoader;

  fThread := TTask.Create
  (
    procedure ()
    begin
      try
        LoDataset := fmxDataModule.ServerMethods.GetLoginDetails(edtEmail.Text, edtPassword.Text);
      except on E:Exception do
        begin
          TThread.Synchronize(TThread.CurrentThread,
            procedure()
            begin
              ShowMessage('The system could not log you in.  Error Details: '+slinebreak+slinebreak+E.Message+slinebreak+slinebreak+'Please try again.');
              HideLoader;
            end
          )
        end;
      end;

      TThread.Synchronize(TThread.CurrentThread,
        procedure()
        begin
          fmxDataModule.LoggedInUser.LoadFromDataset(LoDataset);
          if fmxDataModule.LoggedInUser.CompanyID.Value > 0 then
          begin
            Toolbarheader.Visible := True;
            lblLoginInfo.Visible := false;
            lblWelcome.Text := 'Welcome ' + fmxDataModule.LoggedInUser.FirstName.Value + ', ' + fmxDataModule.LoggedInUser.LastName.Value;
            GoToProfilesTab.Execute;
            GenerateProfiles;
            pnlButtons.Visible := True;
            fLoggedIn := True;
            FormResize(nil);
          end else
          begin
            lblLoginInfo.Visible := True;
            lblLoginInfo.Text := 'User does not exist, or login details invalid';
          end;
        end
      );

      HideLoader;
    end
  );

  fThread.Start;

这是ShowLoader的代码:

procedure TfrmLogin.CreateLoaderForm;
begin
  if Assigned(fLoader) then
    FreeAndNil(fLoader);
  fLoader := TfrmLoader.Create(Self);
  floader.Parent := Self;
  fLoader.Left := Self.Left + (Self.Width div 2) - (fLoader.Width div 2);
  fLoader.Top  := Self.Top + (Self.Height div 2) - (fLoader.Height div 2);
  fLoader.Show;
end;

procedure TfrmLogin.ShowLoader;
begin

  pnlMain.Enabled := false;
  pnlMain.Opacity := 0.4;

  TTask.Create (
    procedure ()
        begin

          TThread.Queue(TThread.CurrentThread,
            procedure()
            begin
              CreateLoaderForm
            end);

        end
    ).Start;

end;

隐藏装载程序:

procedure TfrmLogin.HideLoader;
begin
  pnlMain.Enabled := True;
  pnlMain.Opacity := 1;
//  pnlMain.Repaint;
  fLoader.Visible := False;
end;

我在上面的代码中缺少什么吗?

另一个问题是为什么我的表单没有在屏幕中间打开?我尝试了不同的方法,在表单属性中设置位置,然后手动进行计算。它总是在设备的左上角打开,但在Windows上可以使用。

1 个答案:

答案 0 :(得分:1)

尝试了类似@nolaspeaker建议的其他方法,并同步了@RemyLebeau建议的用户名和密码字段之后,我删除了另一个线程中的表单加载器,问题仍然存在。这样做很明显,我最初发布的以下代码一定有问题,只是重构了一点:

TThread.CreateAnonymousThread
  (
    procedure
    var
      LsUsername,LsPassword:String;
    begin
      try
        TThread.Synchronize(TThread.CurrentThread,
          procedure()
          begin
            LsUsername := edtEmail.Text;
            LsPassword := edtPassword.Text;
          end
        );

        LoDataset := fmxDataModule.ServerMethods.GetLoginDetails(LsUsername, LsPassword);
      except on E:Exception do
        begin
          TThread.Synchronize(TThread.CurrentThread,
            procedure()
            begin
              ShowMessage('The system could not log you in.  Error Details: '+slinebreak+slinebreak+E.Message+slinebreak+slinebreak+'Please try again.');
              HideLoader;
            end
          )
        end;
      end;


      TThread.Synchronize(TThread.CurrentThread,
        procedure()
        begin
          fmxDataModule.LoggedInUser.LoadFromDataset(LoDataset);
          if fmxDataModule.LoggedInUser.CompanyID.Value > 0 then
            GoToProfilesTab.Execute
          else
          begin
            lblLoginInfo.Visible := True;
            lblLoginInfo.Text := 'User does not exist, or login details invalid';
          end;
        end
      );

      HideLoader;
    end
  ).Start;

再尝试几次调试我在TTabControl.SetActiveTabWithTransition中遇到的情况。

此行出现问题

LocalAnimateIntWait(Layout2, 'Position.X', Round(P.X), Duration, TAnimationType.In,
              TInterpolationType.Linear);

在此代码块中:

procedure TTabControl.SetActiveTabWithTransition(const ATab: TTabItem; ATransition: TTabTransition;
const ADirection: TTabTransitionDirection = TTabTransitionDirection.Normal);

  ...
begin
  case ATransition of
    TTabTransition.Slide:
      begin
        FTransitionRunning := True;
        ClipChildren := True;
        try
          ...

          if ADirection = TTabTransitionDirection.Normal then
          begin
            P...
          end
          else
          begin
            ...


            LocalAnimateIntWait(Layout2, 'Position.X', Round(P.X), Duration, TAnimationType.In,
              TInterpolationType.Linear);


          end;
        finally
          SetLength(FTransitionTabs, 0);
          ClipChildren := False;
          FTransitionRunning := False;
          Realign;
        end;
        // Force repaint
        Application.ProcessMessages;
      end
  else
    ActiveTab := ATab;
  end;
end;

因此,我删除了该单击的选项卡过渡,它最终按预期工作。在我将过渡放回Slide的那一刻,它再次冻结在该行上。我一定会报告此问题。