【发布时间】:2017-10-22 15:16:24
【问题描述】:
我希望有人向我解释什么是 Device.BeginInvokeOnMainThread,它的用途是什么?
还有一些使用它的例子。
【问题讨论】:
标签: multithreading xamarin xamarin.forms
我希望有人向我解释什么是 Device.BeginInvokeOnMainThread,它的用途是什么?
还有一些使用它的例子。
【问题讨论】:
标签: multithreading xamarin xamarin.forms
只是添加一个例子。
假设您有一个异步方法DoAnyWorkAsync,如果您这样调用它(仅作为示例):
DoAnyWorkAsync().ContinueWith ((arg) => {
StatusLabel.Text = "Async operation completed...";
});
StatusLabel 是您在 XAML 中的标签。
一旦异步操作完成,上面的代码将不会在标签中显示消息,因为回调位于与 UI 线程不同的另一个线程中,因此它无法修改 UI。
如果您更新相同的代码,只需将StatusLabel 文本更新包含在Device.BeginInvokeOnMainThread 中,如下所示:
DoAnyWorkAsync().ContinueWith ((arg) => {
Device.BeginInvokeOnMainThread (() => {
StatusLabel.Text = "Async operation completed...";
});
});
不会有任何问题。
自己尝试一下,将DoAnyWorkAsync() 替换为Task.Delay(2000)。
【讨论】:
简单的答案是:后台线程不能修改 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 都不是线程安全的。
代码在主线程返回主循环处理事件时执行。
对于安卓:
很多人认为 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.
}
}
Xamarin.Android BeginInvokeOnMainThread() 方法的实际实现可以在 AndroidPlatformServices.cs 类中找到
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(action) 不会立即执行您的操作代码。它被添加到 Looper 的消息队列中,并在 UI 线程计划处理其消息时进行处理。
【讨论】:
IPlatformServices
您只能从主 UI 线程更新 UI。如果您在后台线程上运行代码并需要更新 UI,BeginInvokeOnMainThread() 允许您强制您的代码在主线程上运行,以便您可以更新 UI。
【讨论】:
如上所述,任何 UI 更新都必须在主线程中进行,否则会发生异常。
虽然 Xamarin.Forms 有一个特殊性,但只要这部分,就可以在主线程之外操作 UI 元素(例如创建 Labels 并将它们添加到 StackLayout 的 Children 集合)而不会出现任何故障的 UI 与当前显示的 UI 元素分离。此方法可用于通过创建 Xamarin.Forms 控件并在单独的线程中设置它们的子/父关系在内存/屏幕外的方式来提高性能,但是为了将它们附加到显示的容器(例如分配 ContentPage's @ 987654325@ 属性)您必须在Device.BeginInvokeOnMainThread() 中执行此操作。
【讨论】: