【问题标题】:What is Device.BeginInvokeOnMainThread for?Device.BeginInvokeOnMainThread 是做什么用的?
【发布时间】:2017-10-22 15:16:24
【问题描述】:

我希望有人向我解释什么是 Device.BeginInvokeOnMainThread,它的用途是什么?

还有一些使用它的例子。

【问题讨论】:

标签: multithreading xamarin xamarin.forms


【解决方案1】:

只是添加一个例子。

假设您有一个异步方法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)

【讨论】:

  • 非常感谢
【解决方案2】:

简单的答案是:后台线程不能修改 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);
}

https://developer.xamarin.com/api/member/Foundation.NSObject.BeginInvokeOnMainThread/p/ObjCRuntime.Selector/Foundation.NSObject/

您在线程中使用此方法来调用在 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 线程计划处理其消息时进行处理。

【讨论】:

  • 只想指出,自从发布此消息以来,android 实现的新位置是here,并在 github 存储库下查找其他平台实现搜索IPlatformServices
【解决方案3】:

您只能从主 UI 线程更新 UI。如果您在后台线程上运行代码并需要更新 UI,BeginInvokeOnMainThread() 允许您强制您的代码在主线程上运行,以便您可以更新 UI。

【讨论】:

    【解决方案4】:

    如上所述,任何 UI 更新都必须在主线程中进行,否则会发生异常。

    虽然 Xamarin.Forms 有一个特殊性,但只要这部分,就可以在主线程之外操作 UI 元素(例如创建 Labels 并将它们添加到 StackLayoutChildren 集合)而不会出现任何故障的 UI 与当前显示的 UI 元素分离。此方法可用于通过创建 Xamarin.Forms 控件并在单独的线程中设置它们的子/父关系在内存/屏幕外的方式来提高性能,但是为了将它们附加到显示的容器(例如分配 ContentPage's @ 987654325@ 属性)您必须在Device.BeginInvokeOnMainThread() 中执行此操作。

    【讨论】:

      猜你喜欢
      • 2017-12-25
      • 1970-01-01
      • 2021-05-29
      • 2011-08-24
      • 2023-04-03
      • 2014-03-04
      • 2012-05-19
      • 2011-10-25
      • 2015-07-17
      相关资源
      最近更新 更多