【问题标题】:Why use Device.BeginInvokeOnMainThread() in a Xamarin application?为什么在 Xamarin 应用程序中使用 Device.BeginInvokeOnMainThread()?
【发布时间】:2017-12-25 16:31:21
【问题描述】:

我的代码如下所示:

    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)) 还有什么作用以及为什么要包含它?

【问题讨论】:

  • 请显示 showCards() 方法的代码或至少告诉我们它的作用

标签: xamarin xamarin.forms


【解决方案1】:

这个Init() 方法所在的类可能是在后台线程上创建的。我假设showCards() 正在更新某种用户界面。 UI 只能在 UI/主线程上更新。 Device.BeginInvokeOnMainThread() 确保 lambda 内的代码在主线程上执行。

ContinueWith() 是一种可以在Task 上找到的方法。如果showCards() 返回一个任务,ContinueWith() 会确保任务在退出 lambda 之前完成。

【讨论】:

    【解决方案2】:

    UI 操作必须在 UI 线程上执行(主线程的名称不同)。如果您尝试从非主线程执行 UI 更改,您的应用程序将崩溃。我认为开发人员希望确保它能够按预期工作。

    【讨论】:

    • 谢谢。调用来自 PhrasesPage 的构造函数,这是一个使用 new NavigationPage(new PhrasesPage()) 打开的导航页面。但我注意到其他导航页面上没有类似的调用,所以我想知道为什么只有一个页面。
    【解决方案3】:

    简单的答案是:后台线程不能修改 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 线程计划处理其消息时进行处理。

    【讨论】:

      猜你喜欢
      • 2017-10-22
      • 2018-12-02
      • 2014-02-07
      • 2018-12-08
      • 1970-01-01
      • 2020-08-30
      • 2016-09-08
      • 2018-04-12
      • 2020-12-11
      相关资源
      最近更新 更多