CSharp:Android App - Asynchronous programming
From Aino Wiki
Contents
Teoria
Async - Await - Task
Con la Programmazione Asincrona a partire da .NET 4.5/C# 5.0, Microsoft ha introdotto le parole chiave async
and await
per migliorare l'esperienza di sviluppo di metodi che richiedono tempi lunghi di esecuzione.
- Un metodo marcato con '
async
' può avere la sua esecuzione sospesa in vari punti del suo codice. La sospensione è marcata con l'operatoreawait
. - L'operatore
await
istruisce il compilatore che l'esecuzione non può continuare oltre un comando asincrono finché il comando (metodo) che è marcato 'awaited', sia terminato. - Metodi asincroni tipicamente restituiscono
Task<T>
, tuttavia essi possono restituire Task (void returns) or void (per event handlers). As opposed to calling await on the async method, it is possible to call await on the return value.
NOTE
-
await
può essere usato solo in un metodo "async
". Se un metodo async non ha l'await il compilatore genererà un warning ed il metodo sarà incredibilmente inefficiente. - i metodi
async void
dovrebbero essere usati per gli handlers. - le eccezioni in un
async void
non possono esser intercettate al di fuori del metodo stesso.
Ci sono due tipologie di applicazioni in cui si può usare questo modello:
- thead di I/O; che useranno
async
conawait
. - thread di CPU; che useranno
Task.Run
conawait
.
Per capire quale tipologia è l'applicazione che ci interessa possiamo dire che:
- se il nostro codice aspetta qualcosa come da un database allora saremo nel caso di thread di I/O.
- se il nostro codice esegue computazioni lunghe e dispendiose allora saremo nel caso di thread di CPU.
DOC
- Operazioni asincrone in Xamarin visualstudiomagazine.com
- Asynchronous programming docs.microsoft.com
- Task-based asynchronous pattern (Progress reporting) docs.microsoft.com
Es
(estratto da [1]):
//... [Activity (Label = "Weather Activity")] public class WeatherActivity : ListActivity { public async protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); var location = Intent.GetStringExtra("Location"); var wr = await GetData (location); ListAdapter = new WeatherAdapter(this, wr.list); } private async Task<WeatherReport> GetData(string location) { string contents; WeatherReport res = new WeatherReport(); string Url = String.Format("http://api.openweathermap.org/data/2.5/forecast/daily?q=" + "{0}&mode=json&units=imperial&cnt=14", location); HttpClient hc = new HttpClient(); Task<string> contentsTask = hc.GetStringAsync(Url); // async method! // L'await che segue restituisce il controllo al chiamante (OnCreate) // ed il task continuerà ad essere eseguito su un altro thread in parallelo contents = await contentsTask; res = JsonConvert.DeserializeObject<WeatherReport>(contents); Parallel.ForEach(res.list, currentWeather => { var url = String.Format("http://openweathermap.org/img/w/{0}.png", currentWeather.weather[0].icon); var imageUrl = new Java.Net.URL(url); Android.Graphics.Bitmap bitmap = Android.Graphics.BitmapFactory.DecodeStream(imageUrl.OpenStream()); currentWeather.weather[0].Image = bitmap; }); return res; } }
Notare i due metodi async (righi 5 e 13) il secondo è di tipo privato ed invocato dal primo al rigo 9 con operatore await
, vuol dire che il chiamante di 'OnCreate' è libero di eseguire oltre. Infine comunque si attenderà l'esecuzione di GetData prima di proseguire col l'istruzione al rigo 10.
Interessante notare l'esecuzione parallela sulla linea 27, ogni esecuzione può essere contemporanea alle altre nel ciclo.
Es. Operazioni in background con UI libera
Thread associati ad operazioni I/O
private readonly HttpClient _httpClient = new HttpClient(); downloadButton.Clicked += async (o, e) => { // This line will yield control to the UI as the request // from the web service is happening. // // The UI thread is now free to perform other work. var stringData = await _httpClient.GetStringAsync(URL); DoSomethingWithData(stringData); };
Thread associati ad operazioni CPU
Da docs.microsoft.com
In un gioco per non interrompere il flusso di immagini visualizzato si vuole eseguire un calcolo (oneroso in CPU) "dei danni" in background. Si avvia un task col metodo Task.Run
e si attende il risultato utilizzando await
.
Rispetto all'esempio iniziale al metodo eseguito dal Task.Run
ho aggiunto anche 2 parametri, occhio al parametro 'progress' perché potrebbe essere agganciato ad una ProgressBar!
CalculateButton.Clicked += async (o, e) => { int parametro1 = 25; var progress = new Progress<int>(progressPercent => pBar.Value = progressPercent); // This line will yield control to the UI while CalculateDamageDone() // performs its work. The UI thread is free to perform other work. var damageResult = await Task.Run(() => CalculateDamageDone(parametro1, progress)); DisplayDamage(damageResult); //Metodo che è sotto }; private DamageResult CalculateDamageDone(string parametro, IProgress<int> progress) { // Code omitted: // // Does an expensive calculation and returns // the result of that calculation. progress = parametro; }
Es Download file da Google Drive
Il seguente esempio non è originale ma preso da un progetto esemplificativo di come funziona l'autenticazione su Google Drive.
using Google.Apis.Auth.OAuth2; using Google.Apis.Auth.OAuth2.Flows; using Google.Apis.Auth.OAuth2.Responses; using Google.Apis.Drive.v3; using Google.Apis.Drive.v3.Data; using Google.Apis.Services; using Google.Apis.Util.Store; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Xamarin.Auth; using Xamarin.Auth.Presenters; namespace CommonLib { public static class AGoogleDriveHelper { public static async void Test() { var id = await CreateFile(); await SaveFile(id, "test", "test save content"); } public static async Task<string> CreateFile() { Google.Apis.Drive.v3.Data.File metadata = new Google.Apis.Drive.v3.Data.File() { Parents = new List<string>() { "root" }, MimeType = "text/plain", Name = "Untitled file" }; var googleFile = await GDriveService.Files.Create(metadata).ExecuteAsync(); if (googleFile == null) { throw new System.IO.IOException("Null result when requesting file creation."); } return googleFile.Id; } public static async Task<FileData> ReadFile(string fileId) { FileData data = new FileData(); var metadata = await GDriveService.Files.Get(fileId).ExecuteAsync(); data.Name = metadata.Name; using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { await GDriveService.Files.Get(fileId).DownloadAsync(ms); ms.Position = 0; using (System.IO.StreamReader sr = new System.IO.StreamReader(ms, Encoding.Default)) { var content = sr.ReadToEnd(); data.Content = content; } } return data; } public static async Task SaveFile(string fileId, string name, string content) { Google.Apis.Drive.v3.Data.File metadata = new Google.Apis.Drive.v3.Data.File() { Name = name }; byte[] byteArray = Encoding.Default.GetBytes(content); await GDriveService.Files.Update(metadata, fileId , new System.IO.MemoryStream(byteArray) , "text/plain").UploadAsync(); } } //TEST Class public class FileData { public string Name { get; set; } public string Content { get; set; } } }
etc
using System; private void Prova() { for (int i = 0; i < 4; i++) { // Commento } }
Mappa e Link
Android App | Android Tips | C#
Visual Studio | MS SQL | Dizionario
Parole chiave: