通过动态不断增加的数据TFDMemTable定义提供的REST API数据,每次在运行时创建TGrid的性能都会降低

如何解决通过动态不断增加的数据TFDMemTable定义提供的REST API数据,每次在运行时创建TGrid的性能都会降低

我正在为iOS和Android开发Firemonkey App。我注意到,每次使用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组件后,对其的引用就会添加到lyt1控件集合中,该控件列出了所有子组件。因此,当您调用FreeAndNil(tgd1);时,只能从全局变量TTeeGrid中释放对tgd1对象的引用,但布局控件集合中的引用仍然保留。而且由于您的TTeeGrid参考计数尚未达到零,因此该对象也不会被破坏。

所以不要使用:

FreeAndNil(tgd1);

您需要使用:

tgd1.DisposeOf;
tgd1 := nil;

这可确保无论对象引用计数如何,都执行TTeeGrid对象的析构函数,这反过来通知Layout TTeeGrid对象被销毁,因此需要将其从Layouts控件中删除集合,因此允许TTeeGrid对象引用计数达到零。

实际上,您应该使用DisposeOf在运行时销毁任何组件。

我建议您在How to free a component in Android / iOS

中阅读有关此主题的更多信息。

EDIT仅在使用ARC的Android和iOS等移动平台上会遇到此问题。在Windows上,您的代码可以正常运行。这可能就是其他人无法重现您的问题的原因。

还请注意,由于在Delphi 10.4 ARC中已删除您的代码,因此也应该可以使用。但是我猜您不是在使用最新版本的Delphi。

您可能要编辑问题并包括您的Delphi版本,以改进此问题,因为所用的Delphi版本会影响问题的答案。

,

坦率地说,我怀疑这里是否有人可以解决您的问题,因为我们无法访问您的REST源,因此其他人无法真正复制该问题。相反,我建议您回溯到my answer here,以解决您先前有关将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提供的一组数据和结构。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-