我已将代码简化为一个简单的示例。
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.
谢谢你, 分光
答案 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>