Search code examples
delphifiremonkey

App hangs by calling same Form 5 times. First 4 times work very nice, fifth time app hangs


I just started developing a mobile app which has two forms, FRM_Main (Main Form) and FRM_Party (Party Form). We can open FRM_Party from FRM_Main by clicking on an Image. Code on Image is:

procedure TFRM_Main.IMAGE_PartyClick(Sender: TObject);
begin
  FRM_Party := TFRM_Party.Create(Application);
  FRM_Party.Show;
end;

Now, when FRM_Party calls the OnActivate event, I am loading some data in a TMSFMXTableView. That code is:

procedure TFRM_Party.FormActivate(Sender: TObject);
var
  TableView : TTMSFMXTableViewItem;
  I : Integer;
begin
  if Class_My_Pro_and_func.Func_DataBaseConnection then    //   Checks wethere database connection is active or not if not then it connect with database and returns bool value.
  Begin
    UniQuery.Close;
    UniQuery.SQL.Clear;
    UniQuery.SQL.Text := 'select * from db_stock.tbl_party where Reg_ID = :Reg_ID and Party_Delete <> :Party_Delete order by Party_Name ';
    UniQuery.ParamByName('Reg_ID').AsInteger := d_Glob_Reg_ID;
    UniQuery.ParamByName('Party_Delete').AsString := 'F';
    UniQuery.Open;

    TABLEVIEW_Party.BeginUpdate;

    if UniQuery.RecordCount > 0 then
    begin
      for I := 1 to UniQuery.RecordCount do
      begin
        TableView :=  TABLEVIEW_Party.Items.Add;
        TableView.Caption := UniQuery.Fields[1].AsString;
        TableView.Description := UniQuery.Fields[2].AsString + ' = ' + UniQuery.Fields[3].AsString;

        TABLEVIEW_Party.EndUpdate;

        UniQuery.Next;
      end;
    end
    else
      ShowMessage('No recored Found.');
  End
end;

On FRM_Party is a Back button which takes the user to FRM_Main. This button code is:

procedure TFRM_Party.BTN_Party_BackClick(Sender: TObject);
begin
  try
     UniQuery.Connection.Close; //   Closing Query connection
     DB_Connection.Disconnect;  //   Disconnecting database
     FreeAndNil(FRM_Party);
     Close;
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;

Apart from the above code, nothing else is written on FRM_Party.

Now, the thing is that when I am running the application on mobile and opening/closing FRM_Party again and again, the first 4 times FRM_Party works very nice, but on the fifth time FRM_Party opens properly, but when closed the app hangs. I tried so many times by closing and opening the app. Every time the result is the same.

Can anyone help me?


Solution

  • In your OnActivate handler, your calls to BeginUpdate() and EndUpdate() on TABLEVIEW_Party are unbalanced if UniQuery.RecordCount is not exactly 1. You are calling BeginUpdate() before entering the loop, which is fine, but then you are calling EndUpdate() inside the loop, thus calling it a different number of times than you call BeginUpdate(). You need to move those calls to the same level, eg:

    procedure TFRM_Party.FormActivate(Sender: TObject);
    var
      TableView : TTMSFMXTableViewItem;
      I : Integer;
    begin
      if not Class_My_Pro_and_func.Func_DataBaseConnection then
        Exit;
    
      UniQuery.Close;
      UniQuery.SQL.Text := 'select * from db_stock.tbl_party where Reg_ID = :Reg_ID and Party_Delete <> :Party_Delete order by Party_Name ';
      UniQuery.ParamByName('Reg_ID').AsInteger := d_Glob_Reg_ID;
      UniQuery.ParamByName('Party_Delete').AsString := 'F';
      UniQuery.Open;
    
      if UniQuery.RecordCount > 0 then
      begin
        TABLEVIEW_Party.BeginUpdate;
        try
          for I := 1 to UniQuery.RecordCount do
          begin
            TableView := TABLEVIEW_Party.Items.Add;
            TableView.Caption := UniQuery.Fields[1].AsString;
            TableView.Description := UniQuery.Fields[2].AsString + ' = ' + UniQuery.Fields[3].AsString;
            UniQuery.Next;
          end;
        finally
          TABLEVIEW_Party.EndUpdate;
        end;
      end
      else
        ShowMessage('No record Found.');
    end;
    

    Also, in your back button's OnClick handler, you are calling Close() on FRM_Party after calling FreeAndNil(FRM_Party).

    On mobile platforms prior to RAD Studio 10.4, object lifetime is managed by ARC. Calling FreeAndNil(FRM_Party) will not actually free the FRM_Party object, it will simply set the FRM_Party variable to nil. The object is not freed because Application still has an active reference to it.

    But in RAD Studio 10.4 onwards, ARC is no longer used, so calling FreeAndNil(FRM_Party) will actually destroy the object. So calling Close() after afterwards will crash your code.

    The correct way to free a TForm when it is closed is to use its OnClose event instead, eg:

    procedure TFRM_Party.FormDestroy(Sender: TObject);
    begin
      If FRM_Party = Self then
        FRM_Party := nil;
    end;
    
    procedure TFRM_Party.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := TCloseAction.caFree;
    end;
    
    procedure TFRM_Party.BTN_Party_BackClick(Sender: TObject);
    begin
      UniQuery.Connection.Close;
      DB_Connection.Disconnect;
      Close;
    end;