I have a simple FMX multi-device C++ app. The app has 1 Form and nothing else. In the Form's OnShow
event, I initiate a function I named StartupCode()
. All of the code is shown below.
When I run the app on Windows, I get expected behavior - the 3 messages pop up in the correct order with the correct timing (e.g. each Sleep()
only starts after the previous dialog box has been acknowledged by clicking OK).
When I run the app on iOS or Android, I get the last message popup first ("Slept 2 sec") but only after the entire time has elapsed (12.25 seconds). Then immediately after acknowledging by clicking OK, I get the "Slept 10 sec" message, and likewise after that I get the "Slept quarter sec" message.
#include <System.SysUtils.hpp>
void StartupCode()
{
Sleep(250);
ShowMessage("Slept quarter sec");
Sleep(10000);
ShowMessage("Slept 10 sec");
Sleep(2000);
ShowMessage("Slept 2 sec");
}
void __fastcall TForm1::FormShow(TObject *Sender)
{
#if defined(_PLAT_IOS) || defined(_PLAT_ANDROID)
TThread::ForceQueue(nullptr, [this](){StartupCode();});
#endif
#if defined(_PLAT_MSWINDOWS)
StartupCode();
#endif
}
I'm green and just trying to learn a bit. I was testing to see if a thread would get into trouble with Apple's app start watchdog timers when I ran into this weird behavior.
Why does this behavior happen?
ShowMessage()
simply behaves differently on mobile platforms in 10.2 Tokyo than what you are expecting.Per the 10.2 Tokyo documentation:
ShowMessage Behaves Differently On Different Platforms
On desktop platforms,
ShowMessage
behaves synchronously. The call finishes only when the user closes the dialog box.On mobile platforms
ShowMessage
behaves asynchronously. The call finishes instantaneously, it does not wait for the user to close the dialog box.If you want to force a specific behavior across different platforms, use
IFMXDialogServiceAsync.ShowMessageAsync
orIFMXDialogServiceSync.ShowMessageSync
from theFMX.Platform
unit.
Per the 10.0 Seattle documentation:
On mobile platforms, calls to
ShowMessage
are not blocking. This means that any code that you place after a call toShowMessage
is executed before the dialog box closes. If you need to execute code after your dialog box closes, useMessageDlg
instead ofShowMessage
.
So, what is happening in your case is that your calls to ShowMessage()
are returning back to StartupCode()
immediately, queuing up the dialogs in the background. Then all of the calls to Sleep()
are processed, and then execution returns back to the main UI loop, which then displays all 3 dialogs at the same time, one on top of the other, with the first one on the bottom and the last one on the top. That is why you see them appear in reverse order.
Note: Android does not support modal/synchronous dialogs! And as such, IFMXDialogServiceSync.ShowMessageSync()
is not implemented on Android, only IFMXDialogServiceAsync.ShowMessageAsync()
is implemented.
Prior to 10.1 Berlin, ShowMessage()
internally called the synchronous version of IFMXDialogService.MessageDialog()
(contrary to what the Seattle documentation above says), which worked on iOS but raised an ENotImplemented
exception on Android.
ShowMessage()
(and MessageDlg()
) was deprecated in 10.1 Berlin.I don't have 10.1 Berlin or 10.2 Tokyo installed to check what ShowMessage()
does internally on those systems, but it sounds like it was updated to use IFMXDialogServiceAsync.ShowMessageAsync()
on both iOS and Android, if you are not getting an ENotImplemented
exception on Android.
Note: the behavior you are seeing has NOTHING to do with TThread::ForceQueue()
(which is broken on Android in 10.2 Tokyo, BTW). Also, TThread::ForceQueue()
does not start a new thread, like you claimed.