xamarin 什么是设备.BeginInvokeOnMainThread?

vom3gejh  于 2023-01-03  发布在  其他
关注(0)|答案(5)|浏览(173)

我希望有人向我解释什么是Device.BeginInvokeOnMainThread,它有什么用?
以及一些使用它的例子。

l7mqbcuq

l7mqbcuq1#

再举个例子。
假设您有一个异步方法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)

f3temu5u

f3temu5u2#

    • 简单的答案是:**后台线程无法修改UI元素,因为iOS和Android中的大多数UI操作都不是线程安全的;因此,需要调用UI线程来执行修改UI的代码,如MyLabel.Text ="NewText"。
    • 详细答案**可在Xamarin文档中找到:

对于iOS:
方法只是调用NSRunLoop. Main.开始调用主线程

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线程计划处理其消息时进行处理。

ldioqlga

ldioqlga3#

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

wj8zmpe1

wj8zmpe14#

如上所述,任何UI更新都必须在主线程中发生,否则将发生异常。
尽管Xamarin有一个特殊之处。形式,可以操纵UI元素(例如,创建Label并将它们添加到StackLayoutChildren集合中),只要UI的这一部分与当前显示的UI元素分离,就不会失败。此方法可用于通过创建Xamarin.Forms控件并在内存中设置其子/父关系来提高性能。在单独的线程中关闭屏幕,但是为了将它们附加到显示的容器(例如,分配ContentPageContent属性),您必须在Device.BeginInvokeOnMainThread()中执行此操作。

busg9geu

busg9geu5#

在分析某些情况下UI线程与后台线程的关系时,需要注意以下几点:
BeginInvokeOnMainThread方法(如docs中所述)仅对调用进行排队,并立即返回给调用方。因此,在这种情况下,UI线程和向UI线程提交了一些工作的后台线程可能并行工作。
但是也有InvokeOnMainThread,如docs中所述,等待UI线程执行方法,直到action指向的代码完成后才返回,所以在这种情况下,后台线程等待UI线程执行完给定的工作,然后继续执行。

相关问题