我的代码如下所示:
public void Init() {
if (AS.pti == PTI.UserInput)
{
AS.runCardTimer = false;
}
else
{
AS.runCardTimer = true;
Device.BeginInvokeOnMainThread(() => showCards().ContinueWith((arg) => { }));
}
}
从构造函数调用Init方法。有人可以向我解释为什么开发人员可能添加了Device.BeginInvokeOnMainThread()而不是只调用showCards方法吗?
还有什么是ContinueWith((arg))做什么以及为什么要包含它?
答案 0 :(得分:3)
可以在后台线程上创建此Init()
方法的类。我假设showCards()
正在更新某种UI。 UI只能在UI /主线程上更新。 Device.BeginInvokeOnMainThread()
确保lambda中的代码在主线程上执行。
ContinueWith()
是一种可以在Task
上找到的方法。如果showCards()
返回任务,ContinueWith()
会确保在退出lambda之前完成任务。
答案 1 :(得分:2)
必须在UI线程上执行UI操作(主线程的名称不同)。如果您尝试从非主线程执行UI更改,您的应用程序将崩溃。我认为开发人员希望确保它能按预期工作。
答案 2 :(得分:0)
简单的答案是:后台线程无法修改UI元素,因为iOS和Android中的大多数UI操作都不是线程安全的;因此,您需要调用UI线程来执行修改UI的代码,例如MyLabel.Text =“New Text”。
详细解答可以在Xamarin文档中找到:
对于iOS:
IOSPlatformServices.BeginInvokeOnMainThread()方法只调用NSRunLoop.Main.BeginInvokeOnMainThread
public void BeginInvokeOnMainThread(Action action)
{
NSRunLoop.Main.BeginInvokeOnMainThread(action.Invoke);
}
您可以从线程中使用此方法来调用UI线程中使用指定选择器公开的指定对象中的代码。 这对于影响UIKit或AppKit的大多数操作都是必需的,因为这些API中没有一个是线程安全的。
当主线程返回其主循环以处理事件时执行代码。
对于Android:
许多人认为Xamarin.Android的BeginInvokeOnMainThread()方法使用Activity.runOnUiThread(),但事实并非如此,并且使用runOnUiThread()和Handler.Post()之间存在差异:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);//<-- post message delays action until UI thread is scheduled to handle messages
} else {
action.run();//<--action is executed immediately if current running thread is UI thread.
}
}
可以在AndroidPlatformServices.cs类中找到Xamarin.Android BeginInvokeOnMainThread()方法的实际实现
public void BeginInvokeOnMainThread(Action action)
{
if (s_handler == null || s_handler.Looper != Looper.MainLooper)
{
s_handler = new Handler(Looper.MainLooper);
}
s_handler.Post(action);
}
https://developer.android.com/reference/android/os/Handler.html#post(java.lang.Runnable) 如您所见,Handler.Post(操作)不会立即执行操作代码。它被添加到Looper的消息队列中,并在UI线程被安排处理其消息时处理。