📜 ⬆️ ⬇️

C # async for iOS and Android

Xamarin added support for C # 5 Async / await on iOS and Android. In addition to the base classes of .NET Async, 174 asynchronous methods appeared in Xamarin.iOS and 337 in Xamarin.Android. Xamarin Mobile , which provides cross-platform access to the address book, camera and geolocation, also became asynchronous. Components with might and main add support for async, for example, cloud backend Parse .

Under the cut decoding and translation of the webinar about this significant event.



')
Programmers want to make responsive applications, so they use asynchronous programming.

Responsive interface responds to user actions no longer than 64 milliseconds, this is not so easy to achieve. Especially if the reaction to user actions requires the execution of long operations. The easiest option is to run a long operation in another thread. And if you use threads, you need to invent your system to coordinate their actions. There are many different approaches and literature on this subject, but each time you need to re-implement it.

Almost any operation on a smartphone can be a long one: working with the file system, scaling a picture, querying the database. And when you download something over the network, you can’t predict how long it will take. And in order to respond to the user no later than 64 milliseconds, we came up with callbacks. They are now very popular with the filing of NodeJS.

If you use many callbacks, the code becomes unreadable and difficult to modify, it is called Callback Hell .



The problem is that the order of execution of this piece of code is not obvious and difficult to understand. We cannot think about functions, cycles, function calls, we start thinking about small blocks of code that other small blocks of code call.

Adding new functions to such code is a very difficult task, I have a headache when I just think about changing this method. And the most interesting is that we still do not handle errors. I don't want to do this, I give up.


Edsger Dijkstra
Our brain is sharpened for understanding static structures, our ability to represent processes that develop over time is poorly developed. Therefore, we must reduce the difference between the program code and the process of its execution in order to understand how the program will be executed by its code as simply as possible.
He meant using GOTO, we encountered the same problem when using callbacks. But progress does not stand still, we create new idioms, new languages, we teach the compiler to do the work for us. And async / await in C # is the new idiom that can replace callbacks.

async Task SnapAndPostAsync() { try { Busy = true; UpdateUIStatus ("Taking a picture"); var picker = new Xamarin.Media.MediaPicker (); var mFile = await picker.TakePhotoAsync (new Xamarin.Media.StoreCameraMediaOptions ()); var tagsCtrl = new GetTagsUIViewontroller (mFile.GetStream ()); // Call new iOS await API await PresentViewControllerAsync (tagsCtrl, true); UpdateUIStatus ("Submitting picture to server"); await PostPicToServiceAsync (mFile.GetStream (), tagsCtrl.Tags); UpdateUIStatus ("Success"); } catch (OperationCanceledException) { UpdateUIStatus ("Canceled"); } finally { Busy = false; } } 

Here is an example of using async / await. This code looks consistent and understandable; adding new features is easy. And the difference is that I added the word async to the function header, which marks the method as asynchronous and then uses await. These keywords say to the compiler: “please do the work for the developer, rewrite this code, divide it into several independent pieces”. In fact, the output will be the same callbacks, the same state control, only made automatically. And you can read the code sequentially, as bequeathed by Dijkstra.

In the example, we used Task <T> . He helps us manage the operations taking place in the background. Task encapsulates task status (in progress, completed, canceled), result and exceptions that occurred during execution. There are two types of Task: not returning a value and returning a value of type T.



With Task you can do a lot of interesting things. If you have several tasks, you can wait until all of them or all of them are completed together. Or chained and executed sequentially.

You can create a Task consisting of other Task. For example, you can run three tasks to retrieve data from different server mirrors and wrap them into one task, which will complete as soon as any of the three completes and returns its result.

Let's look at another example. I really like his sequence. This method downloads the file, if it is already on the disk, we ask the user to overwrite the file or not, if he says “yes” - we will overwrite the file, otherwise we will stop working.

 async Task DownloadFile (Uri uri, string target) { if (File.Exists (target)){ switch (await ShowAlert ("File Exists", "Do you want to overwrite the file?", "yes", "no")){ case 0: break; case 1: return; } } var stream = await Http.Get (uri); await stream.SaveTo(target); } 


As you remember in iOS and Android, the result of the system message comes to us in a callback, that is, without async, we needed to create a message, configure it, assign a callback. In the case of async, the compiler does all this for us. And it is beautiful.

Let's see how ShowAlert is implemented inside Xamarin.
 public static Task<int> ShowAlert(string title, string message, params string [] buttons) { var tcs = new TaskCompletionSource<int> (); var alert = new UIAlertView { Title = title, Message = message }; foreach (var button in buttons) alert.AddButton (button); alert.Clicked += (s, e) => tcs.TrySetResult (e.ButtonIndex); alert.Show (); return tcs.Task; } 

Pay attention to the third line from the end, this is where the magic happens. So we return the result in asynchronous methods. Of course, displaying a system message is a very simple function, but I think it will give you inspiration to write your own.

As I already mentioned, Xamarin supports .NET5 Async base classes and specialized methods for mobile platforms. To be precise, Xamarin.iOS provides 174 asynchronous methods, and Xamarin.Android - 337. In addition, Xamarin Mobile , which provides cross-platform access to the address book, camera and geolocation, became asynchronous. Many components also become asynchronous, for example, cloud backend Parse .

If an API method call can take more than 50 milliseconds, we make its asynchronous version. Depending on the context, you can use either the synchronous or asynchronous version of the method.

Async / await support is exactly the situation when Xamarin greatly simplifies working with iOS and Android platforms and stops being just a wrapper.

(approx. Trans. begins part of Craig Dunn, it seems, he retells the Task-based Asynchronous Pattern )

Let's look at larger examples. All code is available on Github .

Notice nested callbacks, error handling in several places, switching to the UI thread when we need to update the user interface. Pff. The code is difficult to read and, especially, modifications.

 public void DownloadHomepage() { var webClient = new WebClient(); webClient.DownloadStringCompleted += (sender, e) => { if(e.Cancelled || e.Error != null) { // do something with error } string contents = e.Result; int length = contents.Length; InvokeOnMainThread (() => { ResultTextView.Text += "Downloaded the html and found out the length.\n\n"; }); webClient.DownloadDataCompleted += (sender1, e1) => { if(e1.Cancelled || e1.Error != null) { // do something with error } SaveBytesToFile(e1.Result, "team.jpg"); InvokeOnMainThread (() => { ResultTextView.Text += "Downloaded the image.\n"; DownloadedImageView.Image = UIImage.FromFile (localPath); }); ALAssetsLibrary library = new ALAssetsLibrary(); var dict = new NSDictionary(); library.WriteImageToSavedPhotosAlbum (DownloadedImageView.Image.CGImage, dict, (s2,e2) => { InvokeOnMainThread (() => { ResultTextView.Text += "Saved to album assetUrl\n"; }); if (downloaded != null) downloaded(length); }); }; webClient.DownloadDataAsync(new Uri("http://xamarin.com/images/about/team.jpg")); }; webClient.DownloadStringAsync(new Uri("http://xamarin.com/")); } 


The same with async / await:

 public async Task<int> DownloadHomepageAsync() { try { var httpClient = new HttpClient(); Task<string> contentsTask = httpClient.GetStringAsync("http://xamarin.com"); string contents = await contentsTask; int length = contents.Length; ResultTextView.Text += "Downloaded the html and found out the length.\n\n"; byte[] imageBytes = await httpClient.GetByteArrayAsync("http://xamarin.com/images/about/team.jpg"); SaveBytesToFile(imageBytes, "team.jpg"); ResultTextView.Text += "Downloaded the image.\n"; DownloadedImageView.Image = UIImage.FromFile (localPath); ALAssetsLibrary library = new ALAssetsLibrary(); var dict = new NSDictionary(); var assetUrl = await library.WriteImageToSavedPhotosAlbumAsync (DownloadedImageView.Image.CGImage, dict); ResultTextView.Text += "Saved to album assetUrl = " + assetUrl + "\n"; ResultTextView.Text += "\n\n\n" + contents; // just dump the entire HTML return length; } catch { // do something with error return -1; } } 


Compare? The new code is linear, easy to read, error handling is collected in one place.

You may also notice HttpClient, a new API object available in Xamarin. In the code we are trying to download the html of the xamarin.com main page with its help. After calling GetStringAsync, a parallel stream is immediately launched, which downloads html from the site. And returns to us Task, the link to this operation. When we call await, we say that we cannot continue without the result of this operation, control is transferred to the main process. When the string is received, control returns to our method on the next line after await, we calculate its length and update the user interface.

When we call httpClient.GetByteArrayAsync, we do not create an intermediate Task, but immediately wait for the result of the query. The image will be downloaded in a different background process, and when it is over the control will return to our method, we will save the image and update the user interface.

The code is consistent and understandable.

I have not done any error handling, but this is easy. It doesn't matter if an error occurs in the code of the method or in the background HttpClient stream, we get it in the catch block. The compiler does this work for us.

I marked different operations in the code with colors, let's compare the old and the new versions:


Notice how inconsistent the old code is, how error handling and reference to the main thread are spread throughout the code. The async / await version is much better in terms of readability and code support.

Moving to async / await


So, how to switch to asynchronous mobile programming? Use the async modifier for methods, lambda expressions, and anonymous functions so that the compiler generates asynchronous code for them. Add the Async suffix to the names of asynchronous methods, this will help other developers to distinguish them from synchronous ones.


Async methods can return void, Task or Task <T>. Void is applicable only for event handlers. If your method returns a value, use Task <T>, otherwise, just Task. If you use Task <T>, just return the value as from the normal method, the compiler will do the rest.

Await can only be used in methods marked as async. Await cannot be used in Catch and Finally blocks.

Error processing


If you call await for a task, any exceptions that occur inside will be thrown into the method of the pending task.

Note: asynchronous methods returning void have nowhere to redirect exceptions, they will appear in the current thread and will cause the application to crash.

 try { //   t    await t; ResultTextView.Text += "** Downloaded " + t.Result.Length + " bytes\n"; } catch(OperationCancelledException) {// t  } catch(Exception exc) { //      t ResultTextView.Text += "--Download Error: " + exc.Messsage + "\n"; } 


Cancel tasks


The active task can be canceled, this is especially important for long tasks, such as working with the network. You can decide to cancel the task from the code or to offer the user the possibility of canceling too long operations.

Cancellation is implemented by passing the Cancellation token parameter to the asynchronous method. You can transfer the created token to other tasks, so you can cancel groups of tasks.

 var cts = new CancellationTokenSource(); var intResult = await DownloadHomepageAsync(cts.Token); //        cts.Cancel(); 


Progress display


The task can inform the pending method of the progress of the operation, for example, transfer the current number of bytes downloaded.
To do this, you must overload the asynchronous method with the version of the host IProgress.

 public class Progress<T> : IProgress<T> { public Progress; public Progress(Action<T> handler); protected virtual void OnReport(T value); public event EventHandler<T> ProgressChanged; //      } 


Combinations of tasks


Multiple tasks can be combined into one using the Task.WhenAny and Task.WhenAll methods . For example, you download several files by combining tasks to download files into one, you can wait until all of them are loaded or continue execution after the first downloaded file.

 while(tasks.Count > 0) { // ,         //t —    var t = await Task.WhenAny(tasks); tasks.Remove(t); try { await t; //   t   ResultTextView.Text += "++ Downloaded " + t.Result.length + " bytes\n"; } catch(OperationCancelledException) {} catch(Exception exc) { //     t ResultTextView.text += "-- Download Error: " + exc.Message + "\n"; } } 


In this example, we download three images from the site, a link to one of them will cause a 403 error. So, we will add all tasks to the tasks list and we will process them in a loop until the list is empty. Create a composite task via Task.WhenAny and wait for it to complete, it will end when any of the nested tasks are completed and return it to us in t. Remove the completed task from the list and execute wait t to get the result of the task. Exceptions that worked during the execution of t will pop up after await is called. Combinations of tasks are a powerful tool for operations of the same type, take a closer look at it.

Read more about C # async in Microsoft documentation , support for iOS and Android APIs on the Xamarin website . Examples of applications in the webinar repository .


Subscribe to our blog (button on the right above). Every Thursday, useful articles about mobile development, marketing and business of mobile studio. The next article (September 12) “How sales are organized in a mobile studio”: our sales manager's confession about processes, reports, megaplanes, self-written CRM and ways of its professional growth.

Source: https://habr.com/ru/post/191926/


All Articles