【问题标题】:Delphi XE5 Android Indy TIDNotify not executedDelphi XE5 Android Indy TIDNotify 未执行
【发布时间】:2023-03-30 01:47:01
【问题描述】:

我已将代码简化为一个简单的示例。 IdTCPClient 读取消息并应将其显示在备忘录中。 附加代码在 Windows 上运行良好,但如果从 ThC_Receive 调用 PostLog,则不会在 android 上执行 DoNotify。如果我通过 Button.ClickMainForm 调用 PostLog,它会被执行。 有什么建议么? TIdSync 有效,但是否推荐?

unit HeaderFooterTemplate;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,   IdContext, IdSync,
  FMX.Layouts, FMX.Memo, IdThreadComponent, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdGlobal;

type
  // ---------------------------------------------------------------------------
  TLog = class(TIdNotify)
  protected
    fMsg: String;
    procedure DoNotify; override;
    //procedure DoSynchronize; override;
  public
    class procedure PostLog(const S: String);
  end;

  // ---------------------------------------------------------------------------
  THeaderFooterForm = class(TForm)
    Header: TToolBar;
    Footer: TToolBar;
    HeaderLabel: TLabel;
    M_Log: TMemo;
    IdTCPClient1: TIdTCPClient;
    ThC_Receive: TIdThreadComponent;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure IdTCPClient1Connected(Sender: TObject);
    procedure ThC_ReceiveRun(Sender: TIdThreadComponent);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  HeaderFooterForm: THeaderFooterForm;

implementation

{$R *.fmx}

// -----------------------------------------------------------------------------
{ TLog }
// -----------------------------------------------------------------------------
procedure TLog.DoNotify;
begin
  HeaderFooterForm.M_Log.Lines.Append(fMsg);
end;

// -----------------------------------------------------------------------------
class procedure TLog.PostLog(const S: String);
begin
  with Create do
  begin
    try
      fMsg := S;
      Notify;
    except
      Free;
      Raise;
    end;
  end;
end;

// -----------------------------------------------------------------------------
procedure THeaderFooterForm.Button2Click(Sender: TObject);
begin
  try
    IdTCPClient1.Host :='192.168.1.12';
    IdTCPClient1.Port := 1001;
    IdTCPClient1.Connect;
  except
      on E: Exception do
      begin
        TLog.PostLog(trim(e.Message));
        Raise;
      end;
  end;
end;

// -----------------------------------------------------------------------------
procedure THeaderFooterForm.IdTCPClient1Connected(Sender: TObject);
begin
  ThC_Receive.Start;
end;

// -----------------------------------------------------------------------------
procedure THeaderFooterForm.ThC_ReceiveRun(Sender: TIdThreadComponent);
var
  s: string;

begin
  try
    s:= (IdTCPClient1.IOHandler.ReadLn(TransmissionSeparator, IndyTextEncoding(IdTextEncodingType.encUTF16LE)));
      TLog.PostLog(trim(s));
  except
    on E: Exception do
    begin
      TLog.PostLog(trim(e.Message));
      Raise;
    end;
  end;
end;

// -----------------------------------------------------------------------------
end.

谢谢你, 光谱

【问题讨论】:

    标签: android delphi indy firemonkey delphi-xe5


    【解决方案1】:

    在工作线程中调用时,TIdNotify 使用 TThread.Queue()TIdSync 使用 TThread.Synchronize(),两者都经过同一个 RTL 队列,因此 TThread.Synchronize() 不太可能工作,但 TThread.Queue()不会。另一方面,在主线程中调用时,不使用TThread(除非将TIdNotify.MainThreadUsesNotify设置为true),而是直接调用TIdNotify.DoNotify()TIdSync.DoSynchronize()。如果TIdNotify 不在线程中工作,则必须破坏TThread.Queue(),这将是Embarcadero 问题,而不是Indy 问题。

    话虽如此,TIdNotify 确实有逻辑在没有TThread.Queue() 可用的 Delphi 版本上使用 TThread.Synchronize()。您可以尝试复制IdSync.pas 并对其进行修改以取消定义Android 上的HAS_STATIC_TThread_Queue 定义,然后将修改后的文件添加到您的项目中。不过,这只在运行时包被禁用时才有效。我不是 Android 开发者,所以不知道 Delphi 如何在移动平台上使用 Packages。

    顺便说一句,IndyTextEncoding(IdTextEncodingType.encUTF16LE) 可以替换为 IndyTextEncoding_UTF16LE

    更新:在 FireMonkey 本身中发现了一个错误(请参阅 QC #123579),可以追溯到 FireMonkey 首次引入时,并且仍然存在于 XE5 更新 2 中。简而言之, FMX.TApplication 不会将处理程序分配给 System.Classes.WakeMainThread 回调(Vcl.TApplication 会)。 TThread 调用 WakeMainThread 通知主线程 Synchronize/Queue() 请求正在等待处理。因此,如果在调用Synchronize/Queue() 时主消息循环处于空闲状态,那么直到稍后如果/当其他东西将新消息放入主消息队列时才会发生任何事情。没有它,TApplication.Idle() 不会被调用,因此CheckSynchronize() 不会被调用来处理待处理的Synchronize/Queue() 请求。这意味着对于后台/非可视进程,Synchronize/Queue() 可能根本无法正常工作,并且偶尔会在 GUI 进程中正常工作。在 Embarcadero 修复该错误之前,一种解决方法是将自定义消息发布到主消息队列以“唤醒”它,或者定期在主线程中手动调用 CheckSynchronize(),例如在计时器中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多