Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Forms - BeginInvokeOnMainThread for an async Action

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?

like image 236
Aidos Avatar asked Sep 24 '16 06:09

Aidos


People also ask

What is device BeginInvokeOnMainThread?

If you are running code on a background thread and need to update the UI, BeginInvokeOnMainThread() allows you to force your code to run on the main thread, so you can update the UI. Follow this answer to receive notifications.

What is async in xamarin forms?

All apps need to perform tasks that may take longer than a few milliseconds. If a task blocks the UI thread for longer than a few seconds, Android will terminate it, crashing the application.

What is xamarin essentials?

Xamarin. Essentials provides a single cross-platform API that works with any iOS, Android, or UWP application that can be accessed from shared code no matter how the user interface is created. See the platform & feature support guide for more information on supported operating systems.


2 Answers

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.

like image 121
Thomas Glaser Avatar answered Sep 28 '22 22:09

Thomas Glaser


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());
}
like image 30
Will Decker Avatar answered Sep 28 '22 21:09

Will Decker