Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Introduzione ai background task

Link copiato negli appunti

Il ciclo di vita di un'applicazione Windows Store presenta notevoli differenze rispetto alle applicazioni "tradizionali". In primo luogo, solo un'applicazione alla volta (o al massimo due, in modalità "snapped") può essere in esecuzione in foreground, mentre eventuali altre app vengono poste in background dal sistema. In secondo luogo, WinRT può sospendere o addirittura terminare le applicazioni in background ogni volta che abbia la necessità di liberare risorse. Per questo motivo, se la nostra app deve svolgere un qualche tipo di attività in background (come ad esempio scaricare un file o aggiornare i tile), è necessario implementare un background task.

Un background task permette infatti ad un'app di svolgere operazioni in background, eventualmente in un processo diverso da quello principale dell'app, anche se quest'ultima si trova nello stato di sospensione. Dall'altro lato, però, un background task viene eseguito in un ambiente controllato dal Windows Runtime e, come vedremo fra breve, può accedere solo a una quantità limitata di risorse di sistema.

Per questo motivo, un background task dovrebbe limitarsi ad eseguire compiti non troppo complessi e che non richiedano l'azione dell'utente. Inoltre meglio evitare di usare un background task per eseguire logica di business o calcoli troppo complessi, viste la ridotta quantità di risorse di sistema a disposizione.

Per creare un background task, è necessario prima di tutto creare un nuovo progetto di tipo Windows Runtime Component (a questo argomento saranno dedicati appositi articoli di approfondimento all'interno di questa guida), come mostrato nella seguente immagine:

Figura 1.


All'interno del progetto del componente, occorre definire una classe non ereditabile (sealed) che implementi l'interfaccia IBackgroundTask e registrarla presso il sistema operativo tramite la classe BackgroundTaskBuilder.

L'implementazione dell'interfaccia IBackgroundTask include un solo metodo dal nome piuttosto auto-esplicativo: Run. Immaginiamo di dover creare un background task che permetta di tracciare il percorso GPS seguito dall'utente anche quando l'app non è in esecuzione in foreground. Il seguente listato mostra un'implementazione di base dell'interfaccia IBackgroundTask.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
namespace Demo.Html.it.BackgroundTask.CS
{
	public sealed class BikeGPSPositionUpdateBackgroundTask : IBackgroundTask
	{
		public void Run(IBackgroundTaskInstance taskInstance)
		{
			// Aggiornamento coordinate GPS
		}
	}
}

Adesso che abbiamo il nostro background task, occorre registrarlo tramite la classe BackgroundTaskBuilder e associare l'evento che scatenerà l'esecuzione del task. Quando questo evento, definito trigger, verrà sollevato, il sistema operativo invocherà il metodo Run. Il prossimo snippet mostra entrambi questi passaggi:

var builder = new BackgroundTaskBuilder();
builder.Name = "bikePositionUpdate";
builder.TaskEntryPoint = "Demo.Html.it.BackgroundTask.CS.BikeGPSPositionUpdateBackgroundTask";
builder.SetTrigger(/* tipo di trigger */);
BackgroundTaskRegistration taskRegistration = builder.Register();

Dopo aver definito una nuova istanza della classe BackgroundTaskBuilder, il codice assegna un nome al background task e definisce l'entry point del task, rappresentato dalla classe che implementa l'interfaccia IBackgroundTask, usando la sintassi namespace.nomedellaclasse. La riga di codice successiva invoca il metodo SetTrigger per associare al background task il trigger (ossia l'evento) che consentirà a WinRT di sapere quando avviare il background task. Quest'ultimo metodo accetta come unico parametro un oggetto che implementa l'interfaccia IBackgroundTrigger. In seguito vedremo quali sono i trigger che WinRT mette a disposizione del nostro background task.

Infine, l'ultima riga di codice procede a registrare il background task presso il sistema operativo tramite il metodo Register della classe BackgroundTaskBuilder; questo metodo restituisce un'istanza di tipo BackgroundTaskRegistration (avremo modo di tornare su questa classe più avanti).

Trigger e condizioni

WinRT mette a disposizione diverse tipologie di trigger che permettono di schedulare l'esecuzione di background task a fronte di specifici eventi. I trigger sono i seguenti:

  • SystemTrigger
  • MaintenanceTrigger
  • TimeTrigger
  • PushNotificationTrigger
  • NetworkOperatorNotificationTrigger
  • NetworkOperatorHotspotAuthenticationTrigger

Alcuni di questi, come SystemTrigger e MaintenanceTrigger hanno una portata più generale, mentre gli altri hanno un campo di applicazione più limitato e specifico.

SystemTrigger

In particolare, il trigger di tipo SystemTrigger viene sollevato a fronte di specifici eventi di sistema (come ad esempio il completamento dell'aggiornamento di un'app, il cambio di fuso orario, l'aggiunta o la rimozione dell'app dal Lock screen, etc.).

Il costruttore della classe SystemTrigger accetta due parametri:

  • il primo parametro, di tipo SystemTriggerType, rappresenta il tipo di trigger di sistema associate al background task,
  • mentre il secondo, denominato oneShot, indica a WinRT se avviare il task una volta soltanto (se impostato a true) ovvero ogni volta che l'evento viene sollevato.

La seguente riga di codice mostra come utilizzare la classe SystemTrigger per definire un background task che verrà eseguito ogni volta che la connessione a internet torna a essere disponibile.

builder.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false));

L'enumerazione completa dell'enum SystemTriggerType è la seguente:

Tipologia di trigger Descrizione
Invalid non rappresenta un tipo di trigger valido.
SmsReceived viene sollevato quando un nuovo SMS è ricevuto dal device mobile
UserPresent viene sollevato quando l'utente torna presente, ossia quando sblocca il Lock screen. Perché questo trigger possa funzionare è necessario che l'app sia aggiunta al Lock screen
UserAway viene sollevato quando l'utente è assente ed entra in funzione il Lock screen. Anche in questo caso è necessario che l'app sia aggiunta al Lock screen perché il background task possa essere effettivamente registrato
NetworkStateChange viene sollevato quando si verifica un cambiamento nella connessione o nei relativi costi
ControlChannelReset viene sollevato quando il control channel viene resettato. È necessario che l'app sia aggiunta al Lock screen perché il background task possa essere effettivamente registrato
InternetAvailable viene sollevato quando la connessione a internet diviene disponibile
SessionConnected viene sollevato quando la sessione è connessa. È necessario che l'app sia aggiunta al Lock screen perché il background task possa essere effettivamente registrato
ServicingComplete viene sollevato quando il sistema operativo ha terminato l'aggiornamento di un'app
LockScreenApplicationAdded viene sollevato quando un'app viene aggiunta al Lock screen
LockScreenApplicationRemoved viene sollevato quando un'app viene rimossa dal Lock screen
TimeZoneChange viene sollevato quando la "time zone" del device viene modificata e nel caso in cui la nuova zona preveda una modifica di fuso orario che comporti una modifica all'ora di sistema.
OnlineIdConnectedStateChange viene sollevato quando l'account Windows Live viene modificato
BackgroundWorkCostChange viene sollevato quando il costo dell'operazione in background si modifica.. È necessario aggiungere l'applicazione al Lock Screen affinchè questo evento possa essere registrato

MaintenanceTrigger

L'altro trigger di carattere generale è quello di tipo MaintenanceTrigger, che può essere utilizzato per schedulare operazioni periodiche da eseguire in background. In questo senso, svolge funzioni analoghe al TimeTrigger, con la differenza che quest'ultimo viene sollevato solo se l'utente ha aggiunto l'app al Lock screen. Un'altra importante differenza è che le attività in background che utilizzano un MaintenaceTrigger vengono eseguite solo quando il device è collegato all'alimentazione (in caso contrario i relativi eventi non vengono sollevati).

Tanto il costruttore della classe MaintenanceTrigger, tanto quello della classe TimeTrigger accettano gli stessi due parametri:

  • il primo, denominato freshnessTime, specifica il numero di minuti che devono trascorrere prima che il background task venga eseguito;
  • il secondo, oneShot, è un Boolean che indica se il trigger deve essere sollevato solo una volta ovvero a intervalli di tempo regolari.

Ad esempio, tramite il seguente codice il background task verrà schedulato per essere eseguito ogni sessanta minuti:

builder.SetTrigger(new MaintenanceTrigger(60, false));

È importante ricordare che WinRT ha un timer interno che esegue i task schedulati ogni 15 minuti.

Questo significa che se freshnessTime viene impostato a 15 minuti e oneShot a false, il task parte alla prossima occorrenza del timer interno (al massimo entro 15 minuti) e viene eseguito ogni 15 minuti. Se il task invece viene registrato con il parametro oneShot a true, il task viene eseguito una sola volta nei 15 minuti dal momento della registrazione.

Vuol dire anche che non è possibile impostare il freshnessTime a un valore inferiore a 15 minuti. In caso contrario, otterremmo un'eccezione di tipo ArgumentException, come mostrato nella prossima immagine:

Figura 2.

Altri trigger

WinRT espone anche altri tipi di trigger specifici:

  • il PushNotificationTrigger, che viene sollevato quando arriva una notifica tramite il Windows Push Notifications Service channel;
  • il trigger NetworkOperatorNotificationTrigger, legato alla notifica di un operatore di rete mobile;
  • il NetworkOperatorHotspotAuthenticationTrigger, che rappresenta un trigger di autenticazione dell'hotspot dell'operatore di rete mobile.

Le API di Windows 8.1 hanno infine aggiunto un ulteriore tipologia di trigger, rappresentata dalla classe LocationTrigger, che permette di eseguire background task in base alla posizione geografica dell'utente (geofencing).

Condizioni

È anche possibile definire condizioni che devono essere verificate dal sistema operativo prima di avviare un determinate background task. La classe BackgroundTaskBuilder espone infatti il metodo AddCondition, il quale permette di aggiungere una singola condizione alla volta, sotto forma di un oggetto di tipo SystemCondition.

builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));

Il costruttore della classe SystemCondition accetta infatti una istanza di tipo SystemConditionType, che enumera le possibili condizioni cui può essere subordinata l'esecuzione di un background task. L'enumerazione include le seguenti possibilità:

Condizione Descrizione
Invalid Non è una condizione valida.
UserPresent il background task può essere eseguito solo se l'utente è presente. Se il trigger viene sollevato durante l'assenza dell'utente, il relativo background task verrà eseguito non appena l'utente tornerà presente.
UserNotPresent il background task può essere eseguito solo se l'utente non è presente. Se il trigger viene sollevato quando l'utente è presente, il relativo background task verrà eseguito non appena l'utente non sta più usando l'applicazione.
InternetAvailable il background task viene eseguito solo se è presente l'accesso a internet. Se il trigger viene sollevato in assenza di una connessione a internet, il relativo background task verrà eseguito non la connessione tornerà disponibile.
InternetNotAvailable il background task viene eseguito solo se manca l'accesso a internet. Se il trigger viene sollevato in presenza di una connessione internet, il relativo background task verrà eseguito non appena la connessione non sarà più disponibile.
SessionConnected il background task viene eseguito solo se la sessione utente è connessa. Se il trigger viene sollevato quando l'utente non ha ancora effettuato il login, il relativo background task verrà eseguito non appena l'utente effettuerà l'accesso.
SessionDisconnected il background task viene eseguito solo se l'utente ha effettuato il logout. Se il trigger viene sollevato quando l'utente è ancora loggato, il relativo background task verrà eseguito non appena l'utente effettuerà il logout.
FreeNetworkAvailable il background task viene eseguito solo quando è disponibile una connessione di rete gratuita.
BackgroundWorkCostNotHigh il background task verrà eseguito solo se il relativo carico di lavoro è basso.

Il seguente snippet mostra come subordinare l'esecuzione di un background task al verificarsi di due condizioni:

builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
builder.AddCondition(new SystemCondition(SystemConditionType.SessionConnected));

Dichiarare il background task nell'application manifest

Affinché la registrazione di un background task sia possibile, è necessario dichiarare la relativa capacità nell'application manifest, indicando anche il tipo di trigger che scatenerà l'evento. La prossima immagine mostra il manifest con la dichiarazione del background task e del relativo trigger.

Figura 3.


Se aprite il manifest con l'editor XML, vedrete che Visual Studio ha aggiunto un'apposita sezione con l'indicazione dell'entry point del background task (nella solita forma namespace.nomedellaclasse) e del tipo di task (systemEvent).

<Extensions>
	<Extension Category="windows.backgroundTasks" EntryPoint="Demo.Html.it.BackgroundTask.CS.BikeGPSPositionUpdateBackgroundTask">
		<BackgroundTasks>
			<Task Type="systemEvent" />
		</BackgroundTasks>
	</Extension>
</Extensions>

Tieni presente che se il tipo di trigger richiede l'interazione con il Lock screen (es. PushNotificationTrigger, TimeTrigger, ecc.), nell'application manifest dovrai anche specificare il modo con cui l'app mostrerà le notifiche sul Lock screen e fornire l'immagine da utilizzare per il badge.

Figura 4.

Enumerare i task registrati

Quando si registra un background task, è importante essere sicuri di farlo una sola volta, altrimenti potresmmo vedere lo stesso task eseguito più volte. Per testare se un task è già stato registrato, è possibile utilizzare la classe BackgroundTaskRegistration e verificare la proprietà Value.Name come mostrato nel seguente snippet:

var taskName = "bikePositionUpdate";
var taskRegistered = false;
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
	if (task.Value.Name == taskName)
	{
		taskRegistered = true;
		break;
	}
}
if (!taskRegistered)
{
	var builder = new BackgroundTaskBuilder();
	builder.Name = taskName;
	builder.TaskEntryPoint = "Demo.Html.it.BackgroundTask.CS.BikePositionUpdateBackgroundTask";
	builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
	builder.AddCondition(new SystemCondition(SystemConditionType.SessionConnected));
	TimeTrigger trigger = new TimeTrigger(10, true);
	builder.SetTrigger(trigger);
	BackgroundTaskRegistration taskRegistration = builder.Register();
}

Il codice esegue un foreach sui background task registrati dall'applicazione sfruttando il relativo dictionary in sola lettura esposto tramite la proprietà AllTasks della classe BackgroundTaskRegistration. Quindi il codice procede a registrare il task corrente, ma solo nel caso in cui un task con lo stesso nome non sia stato già registrato presso il sistema operativo, evitando così di eseguire più volte lo stesso codice in background.

Eseguire codice asincrono in un background task

Se il codice da eseguire in background è asincrono, è necessario usare un deferral all'interno del metodo Run, sfruttando il metodo GetDeferral esposto dall'oggetto di tipo IBackgroundTaskInstance ricevuto come parametro, come mostrato nel seguente codice:

public sealed class BikeGPSPositionUpdateBackgroundTask : IBackgroundTask
{
	public async void Run(IBackgroundTaskInstance taskInstance)
	{
		var deferral = taskInstance.GetDeferral();
		// codice asincrono con await
		deferral.Complete();
	}
}

Dal momento che si tratta di codice asincrono, utilizzeremo il pattern async/await per eseguire l'operazione asincrona (notate la parola chiave async nella firma del metodo) e, al termine dell'operazione, marcheremo il deferral come completato tramite una chiamata al metodo Complete. È importante ricordare di eseguire tutto il codice tra la richiesta del deferral e la chiamata al metodo Complete, altrimenti il sistema assumerà che l'operazione è stata completata e che il thread può essere distrutto.

Nelle prossime lezioni scenderemo con esempi pratici in varie casistiche legate ai background task.

Ti consigliamo anche