Delphi Can
ListView Veri doldurma esnasında TTask kullanımı - Baskı Önizleme

+- Delphi Can (https://www.delphican.com)
+-- Forum: Delphi (https://www.delphican.com/forumdisplay.php?fid=3)
+--- Forum: Mobil Platform - FireMonkey (FMX) (https://www.delphican.com/forumdisplay.php?fid=7)
+--- Konu Başlığı: ListView Veri doldurma esnasında TTask kullanımı (/showthread.php?tid=5724)



ListView Veri doldurma esnasında TTask kullanımı - kornakamil - 13-02-2021

küçük 3500- 4000 kayıtlık bir veritabanım var . Veritabanı bilgilerini ListViewe görsel olarak bağladım ve senkronize ettim . Veri sayısı küçük olduğu için senkronizasyon işlerimi kolaylaştırıyor. Problemim şu . Veritabanını Listviewe yüklerken TTask kullanıyorum . TTask içinde veritabanı bağlantısı kurulup Table açıldığında TTask sonlanıyor. Yükleme esnasında oluşturduğum Frame doğal olarak işlevini tamamlıyor. Verile Listviewe yüklenirken yaklaşık 30 saniye kadar verilerin gelmesini bekliyorum . Görsel olarak hoş olmuyor. Listview tüm veritabanıma yükleninceye TTask işlemimin devamını nasıl sağlıyabilirim . Tam olarak problemimi anlatabilmişimdir umarım . Yardımlarınız için şimdiden teşekkür ederim ..


ListView Veri doldurma esnasında TTask kullanımı - kornakamil - 14-02-2021

Sanırım sorunun çözümünü FGX bileşeni sağlayacak. Abdullah hocam Sydney içinde tekrar paketleseniz bileşeni. İnternette ve sitede önerilen çözümleri deniyorum fakat istediğim akıcılıkta bir sonuç elde edemedim ..


ListView Veri doldurma esnasında TTask kullanımı - mrmarman - 14-02-2021

Veritabanı bağlantısını görsel olarak kurup, ne kadar kontrolünüzde olduğunu bilemediğim ve ayrıca ttask içinde bu veri tabanı bağlantısını nasıl zapt ettiğinizi ve listview'e aktardığınızı bilemiyorum.

Görsel bağlantı yerine kod ile veritabanına bağlanırsanız, (burada bunun üzerinde yorum yapılabilir) yazacağınız procedure ve callback yapıları size tthread aracılığıyla bu paralelleştirme imkanını kolaylaşır.


ListView Veri doldurma esnasında TTask kullanımı - kornakamil - 14-02-2021

ListView verileri kodla doldurdum.. Veritabanı ilişkisinide Locate ile sağladım item seçildikçe . Yüklenme hızı 4-5 kat arttı . Ama Listview görüntülenmeden biraz kasılma hala var . . Kod yazarken kolaya kaçmamak lazım bunun önemini daha iyi gördüm . Hız olarak çok farketti . Yardımlarınız için teşekkür ederim


ListView Veri doldurma esnasında TTask kullanımı - mrmarman - 14-02-2021

Şöyle bir örnek proje hazırladım sizin için.... Anlatılanlar ete kemiğe bürünsün diye örneklemek istedim. Hem başlığı okuyanlar da fiziki bir örnek elde etmiş olurlar.

Örnek Proje RAR ile paketli bu mesaj ekindedir.  Idea
  • RESOURCE olarak test içerik 50 kayıtlık bir veri içeriyor.  (bu test listesini 10 kere döndürürseniz 500 kayıtmış gibi test edebilirsiniz)
  • Hem Android hem de Windows ortamında deneyebilirsiniz.
  • İşlem bilgilendirmeleri Notification olarak karşınıza geliyor. 
Dikkat edileceği üzere Veritabanı için gerekli Query nesnesi Thread içerisinde oluşturarak işi bitince de free ediliyor. Yani forma bırakılmış bir nesne değil. 

Thread işlemlerinde bağımsızlaştırılmış işlemler önemlidir. Bilgilendirme için kullanılan aNotif procedure de (bu örnekte gerek yoktu aslında dinamik procedure'ler değil de asıl projenizdeki diğer nesneler ile iletişim için Syncronize kullanımı önemli) Syncronize ile çağrıldı. 

Çok önemli not :
  • Kullanılan örnekte SQLite veritabanı kullanımıştır. 
  • Yapısal olarak sorgulama için ( SELECT ) birden fazla paralel erişim için uygun olsa da kayıt girişi veya değişikliği için (INSERT, UPDATE) tek kullanıcılı işlemler için uygundur. 
  • Bu detayı da bildirmiş olayım.  Idea
 NOT : EDIT : Düzeltilmiş Kod...


Uses  System.IOUtils,
      System.Notification,
      Data.DB,
      FireDAC.Stan.Intf,
      FireDAC.Comp.UI,
      FireDAC.FMXUI.Wait, {FDGUIxWaitCursor.Provider := 'FMX'}
      FireDAC.DApt,
      FireDAC.Comp.Client,
      FireDAC.Stan.Param,
      FireDAC.Stan.Def,
      FireDAC.Stan.Async,
      FireDAC.Phys.Intf,
      FireDAC.Phys.SQLiteWrapper.Stat,
      FireDAC.Phys.SQLite;

Var
  FDGUIxWaitCursor          : TFDGUIxWaitCursor;
  FDPhysSQLiteDriverLink    : TFDPhysSQLiteDriverLink;
  FDGUIxAsyncExecuteDialog  : TFDGUIxAsyncExecuteDialog;
  FDBFileName : String;

procedure   TForm1.DB_Prepare( dbName: String = 'dbSqlite.db' );
Var
  LConnection : TFDConnection;
  LQuery      : TFDQuery;
  LRes        : TResourceStream;
begin
  {$if Defined(MSWINDOWS)}
    FDBFileName := System.IOUtils.TPath.Combine( TPath.Combine(GetCurrentDir, 'DATA'), dbName);
  {$elseif Defined(ANDROID)}
    FDBFileName := TPath.Combine( TPath.Combine( TPath.GetDocumentsPath, 'DATA'), dbname);
  {$endif}

  if NOT DirectoryExists ( ExtractFilePath(FDBFileName))
    then ForceDirectories( ExtractFilePath(FDBFileName) );

  aNotif( 'DB_Background', 'DB Path', FDBFileName );

  if NOT FileExists( FDBFileName ) then
  begin
    LConnection := TFDConnection.Create( nil );
    LConnection.Params.Values['DriverID'] := 'SQLite';
    LConnection.Params.Values['database'] := FDBFileName;

    LQuery      := TFDQuery.Create( nil );
    try
      LQuery.Connection := LConnection;
      LQuery.SQL.Text   :=  ''
                          + 'CREATE TABLE ''OrnekTable'' ('
                          + ' ''id''      INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,'
                          + ' ''Ad''      varchar(255) default NULL,'
                          + ' ''DogTar''  varchar(255),'
                          + ' ''Adres''   varchar(255) default NULL,'
                          + ' ''Telefon'' varchar(100) default NULL )'
                          ;
      try
        LQuery.ExecSQL;
      except
        aNotif( 'DB_Background', 'DB Cretation', 'CREATE Edilemedi...' );
        Exit;
      end;

      LRes := TResourceStream.Create( HInstance, 'SampleData', RT_RCDATA );
      try
        LQuery.SQL.LoadFromStream( LRes );
        LQuery.ExecSQL;
      finally
        FreeAndNil(LRes);
      end;
      LConnection.Commit;
    finally
      FreeAndNil(LQuery);
      FreeAndNil(LConnection);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DB_Prepare();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TThread.CreateAnonymousThread(procedure ()
  var
    LQuery  : TFDQuery;
    LConnection : TFDConnection;
  begin
    LConnection := TFDConnection.Create(nil);
    LQuery      := TFDQuery.Create( nil );
    try
      LQuery.Connection := LConnection;
      LConnection.Params.Values['DriverID'] := 'SQLite';
      LConnection.Params.Values['database'] := FDBFileName;

      LQuery.SQL.Text   :=  'SELECT * FROM  ''OrnekTable'' ';
      LQuery.Active     := True;
      while NOT LQuery.Eof do
      begin
        TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
            ListView1.Items.Add.Text := Format( '%s : %s', [ LQuery.FieldByName('Ad').AsString, LQuery.FieldByName('DogTar').AsString ] );
          end);
        LQuery.Next;
      end;
      LQuery.Active     := False;
    finally
      FreeAndNil(LQuery);
      FreeAndNil(LConnection);
    end;

    TThread.Synchronize (TThread.CurrentThread,
      procedure ()
      begin
        aNotif( 'DB_Background', 'DB Background', 'DB ListView aktarma bitti...' );
      end);
  end).Start;
end;

procedure TForm1.aNotif( aName, aTitle, aBody : String );
var
  LNotification: System.Notification.TNotification;
begin
  With System.Notification.TNotificationCenter.Create(nil) do
  try
    if Supported then begin
      LNotification := CreateNotification;
      try
        LNotification.Name       := aName;
        LNotification.Title      := aTitle;
        LNotification.AlertBody  := aBody;
        PresentNotification(LNotification);
      finally
        LNotification.Free;
      end;
    end;
  finally
    Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown     := true;

  FDGUIxWaitCursor                := TFDGUIxWaitCursor.Create(nil);
  FDPhysSQLiteDriverLink          := TFDPhysSQLiteDriverLink.Create(nil);
  FDGUIxAsyncExecuteDialog        := TFDGUIxAsyncExecuteDialog.Create(nil);

  FDPhysSQLiteDriverLink.DriverID := 'SQLite';
  FDGUIxWaitCursor.Provider       := 'FMX';
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FDGUIxWaitCursor);
  FreeAndNil(FDPhysSQLiteDriverLink);
  FreeAndNil(FDGUIxAsyncExecuteDialog);
end;





ListView Veri doldurma esnasında TTask kullanımı - kornakamil - 14-02-2021

Üstad örnek harika kafamdaki tüm soruların cevabı örneğin içinde .
Ben kodlamaya Clipper ile başlamıştım . Kod yazmaktan yüksünmem
ama sanırım Visual proğramlar bizi biraz tembelliğe alıştırdı.
Emeğinize sağlık. Saygılar


ListView Veri doldurma esnasında TTask kullanımı - mrmarman - 14-02-2021

@Tuğrul HELVACI çok haklı olarak, çok ama çok önemli hatama dikkatimi çekti.

Acele ile yazınca oluyor böyle hatalar ama bu fahiş hata sayılanlardan, yani özürü yok  Idea


MemoryLeak bir projede en kaçınılması gereken hatalardan biridir. Buradaki örnekte OnCreate olayına konacak kısmı Button altına koyunca her butona basışta var olan nesneye bir yenisi eklenerek şişiyor olduğunu fark etmemişim.

Ayrıca Thread içerisinde aynı connection'u kullanmışım, ona da bir ek connection create edilmesi sağlandı. Idea

Kaynak kodun üzerini çizerek yenisini yolluyorum.


Uses  System.IOUtils,
      System.Notification,
      Data.DB,
      FireDAC.Stan.Intf,
      FireDAC.Comp.UI,
      FireDAC.FMXUI.Wait, {FDGUIxWaitCursor.Provider := 'FMX'}
      FireDAC.DApt,
      FireDAC.Comp.Client,
      FireDAC.Stan.Param,
      FireDAC.Stan.Def,
      FireDAC.Stan.Async,
      FireDAC.Phys.Intf,
      FireDAC.Phys.SQLiteWrapper.Stat,
      FireDAC.Phys.SQLite;

Var
  FDGUIxWaitCursor          : TFDGUIxWaitCursor;
  FDPhysSQLiteDriverLink    : TFDPhysSQLiteDriverLink;
  FDGUIxAsyncExecuteDialog  : TFDGUIxAsyncExecuteDialog;
  FDBFileName : String;

procedure   TForm1.DB_Prepare( dbName: String = 'dbSqlite.db' );
Var
  LConnection : TFDConnection;
  LQuery      : TFDQuery;
  LRes        : TResourceStream;
begin
  {$if Defined(MSWINDOWS)}
    FDBFileName := System.IOUtils.TPath.Combine( TPath.Combine(GetCurrentDir, 'DATA'), dbName);
  {$elseif Defined(ANDROID)}
    FDBFileName := TPath.Combine( TPath.Combine( TPath.GetDocumentsPath, 'DATA'), dbname);
  {$endif}

  if NOT DirectoryExists ( ExtractFilePath(FDBFileName))
    then ForceDirectories( ExtractFilePath(FDBFileName) );

  aNotif( 'DB_Background', 'DB Path', FDBFileName );

  if NOT FileExists( FDBFileName ) then
  begin
    LConnection := TFDConnection.Create( nil );
    LConnection.Params.Values['DriverID'] := 'SQLite';
    LConnection.Params.Values['database'] := FDBFileName;

    LQuery      := TFDQuery.Create( nil );
    try
      LQuery.Connection := LConnection;
      LQuery.SQL.Text   :=  ''
                          + 'CREATE TABLE ''OrnekTable'' ('
                          + ' ''id''      INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,'
                          + ' ''Ad''      varchar(255) default NULL,'
                          + ' ''DogTar''  varchar(255),'
                          + ' ''Adres''   varchar(255) default NULL,'
                          + ' ''Telefon'' varchar(100) default NULL )'
                          ;
      try
        LQuery.ExecSQL;
      except
        aNotif( 'DB_Background', 'DB Cretation', 'CREATE Edilemedi...' );
        Exit;
      end;

      LRes := TResourceStream.Create( HInstance, 'SampleData', RT_RCDATA );
      try
        LQuery.SQL.LoadFromStream( LRes );
        LQuery.ExecSQL;
      finally
        FreeAndNil(LRes);
      end;
      LConnection.Commit;
    finally
      FreeAndNil(LQuery);
      FreeAndNil(LConnection);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DB_Prepare();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TThread.CreateAnonymousThread(procedure ()
  var
    LQuery  : TFDQuery;
    LConnection : TFDConnection;
  begin
    LConnection := TFDConnection.Create(nil);
    LQuery      := TFDQuery.Create( nil );
    try
      LQuery.Connection := LConnection;
      LConnection.Params.Values['DriverID'] := 'SQLite';
      LConnection.Params.Values['database'] := FDBFileName;

      LQuery.SQL.Text   :=  'SELECT * FROM  ''OrnekTable'' ';
      LQuery.Active     := True;
          TThread.Synchronize (TThread.CurrentThread,
            procedure ()
            begin
              aNotif( 'DB_Background', 'Rescord Count', InttoStr(LQuery.RecordCount) );
            end);
      while NOT LQuery.Eof do
      begin
        TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
            ListView1.Items.Add.Text := Format( '%s : %s', [ LQuery.FieldByName('Ad').AsString, LQuery.FieldByName('DogTar').AsString ] );
          end);
        LQuery.Next;
      end;
      LQuery.Active     := False;
    finally
      FreeAndNil(LQuery);
      FreeAndNil(LConnection);
    end;

    TThread.Synchronize (TThread.CurrentThread,
      procedure ()
      begin
        aNotif( 'DB_Background', 'DB Background', 'DB ListView aktarma bitti...' );
      end);
  end).Start;
end;

procedure TForm1.aNotif( aName, aTitle, aBody : String );
var
  LNotification: System.Notification.TNotification;
begin
  With System.Notification.TNotificationCenter.Create(nil) do
  try
    if Supported then begin
      LNotification := CreateNotification;
      try
        LNotification.Name       := aName;
        LNotification.Title      := aTitle;
        LNotification.AlertBody  := aBody;
        PresentNotification(LNotification);
      finally
        LNotification.Free;
      end;
    end;
  finally
    Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown     := true;

  FDGUIxWaitCursor                := TFDGUIxWaitCursor.Create(nil);
  FDPhysSQLiteDriverLink          := TFDPhysSQLiteDriverLink.Create(nil);
  FDGUIxAsyncExecuteDialog        := TFDGUIxAsyncExecuteDialog.Create(nil);

  FDPhysSQLiteDriverLink.DriverID := 'SQLite';
  FDGUIxWaitCursor.Provider       := 'FMX';
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FDGUIxWaitCursor);
  FreeAndNil(FDPhysSQLiteDriverLink);
  FreeAndNil(FDGUIxAsyncExecuteDialog);
end;