簡體   English   中英

每次在運行時通過動態(增加數據)TFDMemTable 定義提供的 REST API 數據創建時,TGrid 的性能會降低

[英]Slow performance of TGrid everytime it is created at runtime through dynamic (increasing data) TFDMemTable definition provided REST API data

我正在為 iOS 和 Android 開發 Firemonkey 應用程序。 我注意到,每次使用 TFDMemTable REST API 數據和結構在運行時創建 TGrid 時,應用程序在 iOS 和 Android 調試中的性能都會變慢。

我已經應用了FreeAndNil(TGrid1); 在它一遍又一遍地創建之前清理 TGrid。

一個值得注意的事件是,每次創建 TGrid 時,行都會增加,固定 7 列,性能變慢。 通常,當我達到 10 行或記錄時會發生這種情況。

我的一個大而真正的快速問題:

您認為導致性能下降的開銷來自哪里?

  1. TGrid — 我已經應用了FreeAndNil(TGrid1); 在它的創建之前。

  2. TFMemTable — 我沒有檢查過這個,我不知道如何

  3. TButton — 觸發創建數據並將數據加載到 TGrid 的按鈕。 大部分代碼都在這里

讓我們假設在這種情況之前所有其他組件都工作正常。 如果您願意,我可以與您分享一些代碼,但請指導我您想看哪一個。

更新 1:最小可重復示例

FMX文件

object Form9: TForm9
  Left = 0
  Top = 0
  Caption = 'MRE TeeGrid Runtime'#13#10
  ClientHeight = 480
  ClientWidth = 294
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object btn1: TButton
    Align = Bottom
    Position.Y = 440.000000000000000000
    Size.Width = 294.000000000000000000
    Size.Height = 40.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 9
    Text = 'CREATE TEEGRID'
    OnClick = btn1Click
  end
  object aniSearchProcess: TAniIndicator
    Position.X = 128.000000000000000000
    Position.Y = 216.000000000000000000
  end
  object lyt1: TLayout
    Align = Client
    Size.Width = 294.000000000000000000
    Size.Height = 440.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 11
  end
  object cur1: TFDGUIxWaitCursor
    Provider = 'FMX'
    Left = 32
    Top = 32
  end
  object dvr1: TFDPhysSQLiteDriverLink
    Left = 88
    Top = 32
  end
  object con1: TFDConnection
    Params.Strings = (
      'DriverID=SQLite')
    Connected = True
    LoginPrompt = False
    Left = 144
    Top = 32
  end
  object loc1: TFDLocalSQL
    Connection = con1
    Active = True
    Left = 200
    Top = 32
  end
  object rsc1: TRESTClient
    Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
    AcceptCharset = 'utf-8, *;q=0.8'
    BaseURL = 
      'https://me6hwinr2k.execute-api.ap-southeast-1.amazonaws.com/v0/d' +
      'bqueries?item-var=9&qty=25'
    Params = <>
    Left = 32
    Top = 112
  end
  object rsq1: TRESTRequest
    Client = rsc1
    Params = <>
    Response = rsp1
    SynchronizedEvents = False
    Left = 32
    Top = 176
  end
  object rsp1: TRESTResponse
    ContentType = 'application/json'
    Left = 32
    Top = 240
  end
  object rsd1: TRESTResponseDataSetAdapter
    Active = True
    Dataset = mtb1
    FieldDefs = <>
    Response = rsp1
    Left = 32
    Top = 304
  end
  object mtb1: TFDMemTable
    Active = True
    FieldDefs = <
      item
        Name = 'Category'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'ID'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Item'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Qty'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Container'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Size'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Ex temporibus dolore consequatur.'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Et cum aut est nostrum...'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'Sequi quibusdam eum.'
        DataType = ftWideString
        Size = 255
      end>
    IndexDefs = <>
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    StoreDefs = True
    Left = 32
    Top = 368
  end
end

FMX 程序

unit Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FireDAC.UI.Intf, FireDAC.FMXUI.Wait, FireDAC.Stan.ExprFuncs,
  FireDAC.Phys.SQLiteDef, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
  FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.SQLite, Data.DB,
  FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, REST.Types,
  FMX.Controls.Presentation, FMX.StdCtrls, FireDAC.Comp.DataSet,
  FireDAC.Comp.Client, REST.Response.Adapter, REST.Client, Data.Bind.Components,
  Data.Bind.ObjectScope, FireDAC.Phys.SQLiteVDataSet, FireDAC.Comp.UI,
  FMXTee.Control, FMXTee.Grid, FMX.Layouts;

type
  TForm9 = class(TForm)
    cur1: TFDGUIxWaitCursor;
    dvr1: TFDPhysSQLiteDriverLink;
    con1: TFDConnection;
    loc1: TFDLocalSQL;
    rsc1: TRESTClient;
    rsq1: TRESTRequest;
    rsp1: TRESTResponse;
    rsd1: TRESTResponseDataSetAdapter;
    mtb1: TFDMemTable;
    btn1: TButton;
    aniSearchProcess: TAniIndicator;
    lyt1: TLayout;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form9: TForm9;
  tgd1: TTeeGrid;

implementation

{$R *.fmx}

procedure TForm9.btn1Click(Sender: TObject);
var
  i, CanvassItemId, e : Integer;
begin
  aniSearchProcess.Visible := True;
  aniSearchProcess.Enabled := True;
  {$IFDEF MSWINDOWS}
    Application.ProcessMessages;
  {$ENDIF}
  {$IF DEFINED(iOS) or DEFINED(ANDROID)}
    Application.HandleMessage;
  {$ENDIF}

  FreeAndNil(tgd1); //free old grid

  //create new grid
  tgd1 := TTeeGrid.Create(lyt1);
  With tgd1 do
  begin
    Parent := lyt1;
    Align := TAlignLayout.Client;
    Margins.Top := 5;
    Margins.Left := 5;
    Margins.Right := 5;
    Margins.Bottom := 5;
    ScrollBars.Visible := True;
    Header.Format.Font.Size := 11;
    Cells.Format.Font.Size := 11;
    TabOrder := 0;
    ScrollBars.Visible := False;
  end;

  con1.StartTransaction;
  try
    //define the API here for duplicate/update, initial click and subsequent clicks
    rsc1.BaseURL := 'https://0rgvn0s0gk.execute-api.ap-southeast-1.amazonaws.com/v0/dbqueries?item-var=1&qty=10';
    rsq1.Execute;
    rsd1.Active := True;
    mtb1.Active;
    tgd1.DataSource := mtb1;
    tgd1.Enabled := True;

    // adjust the column properties dynamically
    with tgd1 do
    begin
      for i := 0 to Columns.Count -1 do
      begin
        if i = 0 then
        begin
          Columns[i].Visible := False; // category column
        end
        else if (i = 1) then
        begin
          Columns[i].Visible := False; // id column
        end
        else if (i = 2) then
        begin
          Columns[i].Width.Value := 120; // item column
        end
        else if (i = 3) then
        begin
          Columns[i].Width.Value := 30; // qty column
        end
        else if (i = 4) then
        begin
          Columns[i].Width.Value := 50; // container column
        end
        else if (i = 5) then
        begin
          Columns[i].Width.Value := 50; // size column
        end
        else
        begin
          Columns[i].Width.Value := 50; // subsequent random columns
        end;
      end;
    end;
  finally
    con1.Commit;
  end;

  aniSearchProcess.Visible := False;
  aniSearchProcess.Enabled := False;
  {$IFDEF MSWINDOWS}
    Application.ProcessMessages;
  {$ENDIF}
  {$IF DEFINED(iOS) or DEFINED(ANDROID)}
    Application.HandleMessage;
  {$ENDIF}

end;

end.

您在這里面臨的問題是,由於 ARC 在 Delphi 中的工作方式,您的TTeeGrid並沒有真正被破壞。

這是為什么?
一旦您將 Parent 設置為tgd1組件, tgd1的引用就會添加到lyt1 Controls 集合中,該集合列出了所有子組件。 所以當你調用FreeAndNil(tgd1); 您只從全局變量tgd1釋放對TTeeGrid對象的引用,但布局控件集合中的引用仍然存在。 並且由於您的TTeeGrid引用計數尚未達到零,因此該對象不會被銷毀。

所以不要使用:

FreeAndNil(tgd1);

你需要使用:

tgd1.DisposeOf;
tgd1 := nil;

這確保了你的TTeeGrid對象的析構函數被執行而不管對象引用計數,這反過來通知布局你的TTeeGrid對象正在被破壞,因此它需要從布局控件集合中刪除,從而允許TTeeGrid對象引用計數達到零.

事實上,您應該使用DisposeOf在運行時銷毀任何組件。

我建議您在如何在 Android / iOS 中釋放組件中閱讀有關此主題的更多信息

編輯 此問題僅在使用 ARC 的 Android 和 iOS 等移動平台上遇到。 在 Windows 上,您的代碼可以正常工作。 這可能是其他人無法重現您的問題的原因。

另請注意,由於在 Delphi 10.4 ARC 中已刪除,您的代碼也應該可以工作。 但我猜你沒有使用最新版本的 Delphi。

您可能希望編輯問題並包含您的 Delphi 版本以改進此問題,因為所使用的 Delphi 版本會影響問題的答案。

坦率地說,我懷疑這里是否有人可以解決您的問題,因為其他人無法真正重現它,因為我們無法訪問您的 REST 源。 相反,我建議您回溯到我在此處回答您之前關於將 TTeeGrid 與 FDMemTable 一起使用的問題。 我建議這樣做的原因是因為它提供了一種對兩個組件進行測試/基准測試的方法,該方法相當獨立,並且不依賴(對其他人)訪問您的 REST 源。 您可以使用如下代碼來調查您報告的速度下降是否與 TTeeGrid 的重復創建/釋放有關(如果是的話,我會感到驚訝)。

procedure TForm1.FormCreate(Sender: TObject);
var
  AField : TField;
begin
  AField := TWideStringField.Create(Self);
  AField.FieldName := 'ID';
  AField.Size := 255;
  AField.FieldKind := fkData;
  AField.DataSet := FDMemTable1;

  { repeat for other fields}

  FDMemTable1.CreateDataSet;
  { insert test data using FDMemTable1.InsertRecord in a loop}

  { repeat the following to see if TTeeGrid really slows down be repeated creation/freeing}
    { create TTeeGrid1 here }
    { connect FDMemTable1 to TTeeGrid1 here}
    {  TTeeGrid1.Free }
  { until done }
end;

在運行時創建 TTeeGrid 會累積開銷,從而在某些時候降低性能。

為了解決這個問題,我在運行時刪除了 TTeeGrid 的創建和釋放,相反,我在設計時放置了 TTeeGrid 可視化組件,並在每次由新數據集觸發時通過啟用為 true 或 false 的屬性刷新其連接和API提供的結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM