Delphi XE5 Android Indy TIDNotify未执行

时间:2014-03-25 14:42:27

标签: android delphi indy firemonkey delphi-xe5

我已将代码简化为一个简单的示例。 IdTCPClient读取消息并将其显示在备忘录中。 附加的代码在Windows上运行正常,但如果从DoNotify调用PostLog,则不会在android上执行ThC_Receive。如果我从Button.Click通过MainForm调用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.

谢谢你, 分光

1 个答案:

答案 0 :(得分:1)

在工作线程中调用时,TIdNotify使用TThread.Queue()TIdSync使用TThread.Synchronize(),两者都通过相同的RTL队列,所以TThread.Synchronize()不太可能{ {1}}可行,但TThread.Queue()不会。另一方面,当在主线程中调用时,TThread未被使用(除非TIdNotify.MainThreadUsesNotify设置为true),而是直接调用TIdNotify.DoNotify()TIdSync.DoSynchronize()。如果TIdNotify在一个帖子中不起作用,那么TThread.Queue()必须被打破,这将是一个Embarcadero问题,而不是Indy问题。

话虽这么说,TIdNotify确实有逻辑在没有TThread.Synchronize()可用的Delphi版本上使用TThread.Queue()。您可以尝试复制IdSync.pas并修改它以取消定义Android上的HAS_STATIC_TThread_Queue定义,然后将修改后的文件添加到项目中。但这只适用于禁用运行时软件包的情况。我不是Android开发人员,因此我不知道Delphi如何在移动平台上使用Package。

BTW,IndyTextEncoding(IdTextEncodingType.encUTF16LE)可以替换为IndyTextEncoding_UTF16LE

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