Xamarin.Forms - BeginInvokeOnMainThread for an async Action

瘦欲@ 提交于 2019-12-20 01:34:54

问题


I am familiar with the rules about updating UI elements on the UI thread using the Device.BeginInvokeOnMainThread, however I have an operation that needs to be run on the UI thread that is actually a Task.

For example, the Push/PopAsync methods on XLabs.Forms.Mvvm seem to behave incorrectly on iOS unless they are invoked on the UI thread. There is also another example in the Acr.UserDialogs library for displaying toasts etc.

I know that making an Action async is basically creating an async void lambda and runs the risk of creating a deadlock in the case of an exception, obviously I don't want this to happen.

Does anybody have a workaround for performing async operations on the UI thread that doesn't involve marking the Action as async?


回答1:


Just make sure you handle exceptions in your Action and you should be fine. The problem you described occurs when you don't handle the exceptions. Below is a very simple example of running an async method from the main thread.

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());
}



回答2:


Since Xamarin.Forms 4.2 there is now a helper method to run tasks on the main thread and await them.

await Device.InvokeOnMainThreadAsync(SomeAsyncMethod);

Several overloads exist that should cover most scenarios:

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);

Related PR: https://github.com/xamarin/Xamarin.Forms/pull/5028

NOTE: the PR had some bug that was fixed in v4.2, so don't use this in v4.1.




回答3:


Dropping this here in case someone wants to await the end of an action which has to be executed on the main thread

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;
    }
}


来源:https://stackoverflow.com/questions/39673153/xamarin-forms-begininvokeonmainthread-for-an-async-action

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!