Xamarin.Forms - BeginInvokeOnMainThread用于异步操作

Aid*_*dos 4 c# async-await xamarin xamarin.forms

我熟悉使用Device.BeginInvokeOnMainThread更新UI线程上的UI元素的规则,但是我有一个操作需要在实际上是Task的UI线程上运行.

例如,XLabs.Forms.Mvvm上的Push/PopAsync方法似乎在iOS上表现不正确,除非在UI线程上调用它们.Acr.UserDialogs库中还有另一个用于显示toasts等的示例.

我知道创建一个Action异步基本上是创建一个异步void lambda并且在异常的情况下冒着创建死锁的风险,显然我不希望这种情况发生.

有没有人在UI线程上执行异步操作的解决方法,不涉及将Action标记为异步?

Raz*_*iel 14

Xamarin.Forms 4.2 开始,现在有一个辅助方法可以在主线程上运行任务并等待它们。

await Device.InvokeOnMainThreadAsync(SomeAsyncMethod);
Run Code Online (Sandbox Code Playgroud)

存在几个应涵盖大多数情况的重载:

System.Threading.Tasks.Task InvokeOnMainThreadAsync (System.Action action);
System.Threading.Tasks.Task InvokeOnMainThreadAsync (System.Func<System.Threading.Tasks.Task> funcTask);
System.Threading.Tasks.Task<T> InvokeOnMainThreadAsync<T> (System.Func<System.Threading.Tasks.Task<T>> funcTask);
System.Threading.Tasks.Task<T> InvokeOnMainThreadAsync<T> (System.Func<T> func);
Run Code Online (Sandbox Code Playgroud)

相关公关:https : //github.com/xamarin/Xamarin.Forms/pull/5028

注意:PR 有一些在 v4.2 中修复的错误,所以不要在 v4.1 中使用它。


Wil*_*ker 7

只要确保你在Action中处理异常,你应该没问题.当您处理异常时,会出现您所描述的问题.下面是从主线程运行异步方法的一个非常简单的示例.

private void Test()
{
    Device.BeginInvokeOnMainThread(SomeMethod);
}

private async void SomeMethod()
{
    try
    {
        await SomeAsyncMethod();
    }
    catch (Exception e) // handle whatever exceptions you expect
    {
        //Handle exceptions
    }
}

private async Task SomeAsyncMethod()
{
    await Navigation.PushModalAsync(new ContentPage());
}
Run Code Online (Sandbox Code Playgroud)


Dbl*_*Dbl 5

如果有人想要等待必须在主线程上执行的操作结束,请在此处删除

public static class DeviceHelper
{
    public static Task RunOnMainThreadAsync(Action action)
    {
        var tcs = new TaskCompletionSource<object>();
        Device.BeginInvokeOnMainThread(
            () =>
            {
                try
                {
                    action();
                    tcs.SetResult(null);
                }
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });

        return tcs.Task;
    }

    public static Task RunOnMainThreadAsync(Task action)
    {
        var tcs = new TaskCompletionSource<object>();
        Device.BeginInvokeOnMainThread(
            async () =>
            {
                try
                {
                    await action;
                    tcs.SetResult(null);
                }
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });

        return tcs.Task;
    }
}
Run Code Online (Sandbox Code Playgroud)