Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

Difference between revisions of "CSharp:Android App - Asynchronous programming"

From Aino Wiki

Jump to: navigation, search
(Mappa e Link)
 
(No difference)

Latest revision as of 15:29, 5 January 2021

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'operatore await.
  • 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 con await.
  • thread di CPU; che useranno Task.Run con await.

Per capire quale tipologia è l'applicazione che ci interessa possiamo dire che:

  1. se il nostro codice aspetta qualcosa come da un database allora saremo nel caso di thread di I/O.
  2. se il nostro codice esegue computazioni lunghe e dispendiose allora saremo nel caso di thread di CPU.

DOC

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

Da docs.microsoft.com

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: