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

Consumare un background task

Link copiato negli appunti

Nell'articolo di introduzione ai background task abbiamo visto come creare un semplice task in un'applicazione Windows Store scritta in HTML5/JavaScript, e come associare l'esecuzione del task al verificarsi di un determinato evento (trigger) e a una o più condizioni. In questo articolo ci spingeremo più nei dettagli, per vedere come monitorare l'esecuzione di un background task, comprendere le limitazioni che il sistema impone, come annullare l'esecuzione di un task e come creare nuove versioni di task in esecuzione.

Monitorare l'esecuzione di un background task

Se un'applicazione ha bisogno di conoscere il risultato dell'esecuzione di un background task, è possibile sfruttare l'evento Completed esposto dalla classe BackgroundTaskRegistration che, come abbiamo già accennato nell'articolo introduttivo, rappresenta il task corrente una volta registrato presso il sistema operativo tramite il metodo Register.

Il seguente listato riprende quanto già visto nell'articolo introduttivo, ma questa volta utilizzando l'evento Completed per segnalare all'utente il completamento del task.

var taskName = "updateGPSPosition";
var taskRegistered = false;
var taskEntryPoint = "js\\updateGPSPositionBackgroundTask.js";
var background = Windows.ApplicationModel.Background;
var iter = background.BackgroundTaskRegistration.allTasks.first();
while (iter.hasCurrent) {
	var task = iter.current.value;
	if (task.name === taskName) {
		taskRegistered = true;
		break;
	}
	iter.moveNext();
}
if (!taskRegistered) {
	var builder = new background.BackgroundTaskBuilder();
	builder.name = taskName;
	builder.taskEntryPoint = taskEntryPoint;
	builder.setTrigger(new background.MaintenanceTrigger(20, false));
	builder.addCondition(new background.SystemCondition(Windows.ApplicationModel.Background.SystemConditionType.internetAvailable));
	var registeredTask = builder.register();
	registeredTask.addEventListener("completed", onCompleted);
}

La relativa callback riceverà come parametro un oggetto di tipo BackgroundTaskCompletedEventArgs. A questo punto, l'handler può aggiornare la user interface, ad esempio notificando all'utente il completamento del task tramite un semplice MessageDialog.

function onCompleted(args) {
	var dialog = new Windows.UI.Popups.MessageDialog("Task completato", "Messaggio da " + args.name);
	dialog.showAsync();
}

È importante sottolineare che, se il background task è stato eseguito (e completato) durante lo stato di sospensione dell'app o dopo che l'app stessa era stata terminata, l'handler dell'evento Completed verrà invocato solo dopo che l'applicazione è stata riattivata dal sistema operativo o lanciata nuovamente dall'utente. Se invece il task è stato concluso quando l'app era ancora in foreground, la callback sarà immediatamente invocata.

Essere a conoscenza del fatto che un task è stato portato a termine non sempre basta. Sarà spesso necessario sapere anche come si è concluso. Per controllare se si sono verificati errori durante l'esecuzione del task, la classe BackgroundTaskCompletedEventArgs espone anche un utile metodo denominato CheckResult , il quale solleva un'eccezione nel caso in cui qualcosa è andata storta durante l'esecuzione del task.

Il prossimo listato mostra un semplice esempio dell'uso di questo metodo, che utilizza un blocco try/catch per intercettare l'eventuale eccezione sollevata dal metodo CheckResult e un semplice MessageDialog per aggiornare l'utente sul risultato.

function onCompleted(args) {
	try {
		args.checkResult();
		var dialog = new Windows.UI.Popups.MessageDialog("Task completato");
		dialog.showAsync();
	}
	catch (ex) {
		var dialog = new Windows.UI.Popups.MessageDialog("Errore durante l'esecuzione del task");
		dialog.showAsync();
	}
}

La prossima immagine mostra il risultato.

Un altro utile evento esposto dalla classe BackgroundTaskRegistration è l'evento Progress che, come il nome suggerisce, permette di seguire l'avanzamento di un'attività. Nel relativo event handler, infatti, possiamo aggiornare la user interface, o aggiornare il tile o il badge dell'applicazione, indicando lo stato di avanzamento del task (ad esempio, specificando la percentuale di completamento delle relative attività).

Il seguente codice mostra come sfruttare questo evento per aggiornare periodicamente il tile dell'applicazione in modo da mostrare lo stato di avanzamento del task.

function onProgress(task, args)
{
	var notifications = Windows.UI.Notifications;
	var template = notifications.TileTemplateType.tileSquareText01;
	var tileXml = notifications.ToastNotificationManager.getTemplateContent(template);
	var tileTextElements = tileXml.getElementsByTagName("text");
	tileTextElements[0].appendChild(tileXml.createTextNode(args.progress + "%"));
	var tileNotification = new notifications.TileNotification(tileXml);
	notifications.TileUpdateManager.createTileUpdaterForApplication().update(tileNotification);
}

Per prima cosa, creiamo il documento XML per il tile, indicando la percentuale di avanzamento del task tramite la proprietà Progress esposta dall'oggetto BackgroundTaskProgressEventArgs ricevuto come parametro dall'handler dell'evento. Quindi utilizziamo il metodo CreateTileUpdaterForApplication della classe TileUpdateManager per aggiornare il tile.

Il valore che rappresenta lo stato di completamento di un task può essere assegnatotramite la proprietà Progress esposta dall'oggetto di tipo WebUIBackgroundTaskInstance che rappresenta il task corrente.

Il seguente snippet mostra un esempio di uso della proprietà Progress per segnalare l'avanzamento del task:

(function () {
	"use strict";
	var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
	function updateGPSPosition() {
		var backgroundTaskDeferral = bgTaskInstance.getDeferral();
		// Prima operazione
		bgTaskInstance.progress = 10;
		// Seconda operazione
		bgTaskInstance.progress = 20;
		// Altre operazioni...
		backgroundTaskDeferral.complete();
		close();
	}
	updateGPSPosition();
})();

Comprendere le limitazioni di un task

Come abbiamo già accennato nell'articolo introduttivo, un background task deve essere necessariamente "leggero" in termini di risorse impiegate. WinRT impone infatti una serie di limiti piuttosto severi all'uso di risorse da parte di un task.

Ad esempio, nel caso di applicazioni che non sono nel Lock screen, la CPU è limitata a un secondo e un task può essere eseguito solo a intervalli di due ore, mentre per applicazioni aggiunte al Lock screen l'intervallo è di 15 minuti (in caso di task basati su MaintenanceTrigger.

L'intervallo è di 15 minuti anche se l'app non è stata aggiunta al Lock screen. In questo caso, tuttavia, il device deve essere alimentato a corrente e non a batteria). Inoltre, se il device è alimentato a batterie, il task incontra dei limiti anche nell'uso della rete, in base allo specifico hardware monetato sul device.

Ad esempio, con una scheda di rete da un 10Mbps, un'app che si trova sul Lock screen può consumare circa 450 MB al giorno, mentre un'app non sul inserita nel Lock screen può consumare 75 MB al giorno (per maggiori dettagli sui limiti ai task si rinvia alla documentazione su MSDN).

Per evitare che questi limiti interferiscano con applicazioni pensate per comunicazioni in real-time, i task basati su ControlChannelTrigger o PushNotificationTrigger ricevono una quota di risorse garantite (sia in termini di CPU che di rete). Queste quote garantite sono indipendenti dall'hardware del device. In altre parole, WinRT considera questi particolari task come "critici", e automaticamente alloca le risorse necessarie alla loro esecuzione.

Se un task eccede i limiti previsti, viene sospeso da WinRT. Puoi controllare se e quante volte il task è stato sospeso ispezionando la proprietà SuspendedCount del task corrente, ad esempio per interrompere l'esecuzione del task dopo un certo numero di sospensioni, come mostrato nel prossimo snippet:

(function () {
	"use strict";
	var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
	function updateGPSPosition() {
		var backgroundTaskDeferral = bgTaskInstance.getDeferral();
		// Prima operazione
		bgTaskInstance.progress = 10;
		if (bgTaskInstance.suspendedCount > 5) {
			return;
		}
		// Altre operazioni...
		backgroundTaskDeferral.complete();
		close();
	}
	updateGPSPosition();
})();

Annullare un task

Quando un task è in esecuzione, normalmente non può essere interrotto. Tuttavia, se il sistema rileva che l'attività in background è inattiva o bloccata (perché ad esempio ci si è dimenticati di invocare il metodo close al termine del codice da eseguire in background), il task viene terminato. Per poter reagire a questa eventualità, l'interfaccia IBackgroundTaskInstance espone uno specifico evento denominato Canceled che viene sollevato quando vi è una richiesta di cancellazione del task.

Il modo più semplice di controllare se un task è stato cancellato consiste nel dichiarare una variabile booleana a livello di classe e impostarla a true nell'handler dell'evento Canceled . Il seguente snippet mostra come controllare l'eventuale richiesta di cancellazione.

(function () {
	"use strict";
	var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
	var cancelRequested = false;
	function updateGPSPosition() {
		bgTaskInstance.addEventListener("canceled", onCanceled);
		var backgroundTaskDeferral = bgTaskInstance.getDeferral();
		// Codice che fa qualcosa
		bgTaskInstance.progress = 10;
		if (cancelRequested) {
			bgTaskInstance.succeeded = false;
			return;
		}
		backgroundTaskDeferral.complete();
		close();
	}
	function onCanceled(cancelSender, cancelReason) {
		cancelRequested = true;
	}
	updateGPSPosition();
})();

All'interno della funzione che contiene il codice da eseguire in background, la prima cosa da fare è quella di sottoscrivere l'evento Canceled . Quindi il codice procede con l'esecuzione del task, controllando la variabile booleana e, nel caso in cui vi sia una richiesta di cancellazione, interrompe l'esecuzione del codice. E' importante sottolineare che l'handler dell'evento Canceled dispone di soli 5 secondi, prima che il task sia terminato dal sistema.

Nel caso in cui l'app abbia bisogno di conoscere se il task è stato completato oppure annullato, possiamo utilizzare lo storage per salvare lo stato del task e successivamente rileggerlo dall'applicazione nell'handler dell'evento Completed relativo al task corrente. Il prossimo snippet mostra questo punto.

(function () {
	"use strict";
	var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
	var cancelRequested = false;
	var localSettings = Windows.Storage.ApplicationData.current.localSettings;
	function updateGPSPosition() {
		bgTaskInstance.addEventListener("canceled", onCanceled);
		var backgroundTaskDeferral = bgTaskInstance.getDeferral();
		// Codice che fa qualcosa
		bgTaskInstance.progress = 10;
		if (cancelRequested) {
			bgTaskInstance.succeeded = false;
			localSettings["status"] = "canceled";
			return;
		}
		backgroundTaskDeferral.complete();
		localSettings["status"] = "success";
		close();
	}
	function onCanceled(cancelSender, cancelReason) {
		cancelRequested = true;
	}
	updateGPSPosition();
})();

Come si vede, prima di interrompere l'esecuzione del task il codice utilizza i LocalSettings, ossia lo storage persistente riservato all'applicazione, per impostare lo stato del task su canceled, nel caso in cui sia stata richiesta la cancellazione del task, ovvero su completed se il task viene eseguito per intero.

Il prossimo listato mostra come recuperare la coppia chiave/valore dai LocalSettings all'interno dell'handler dell'evento Completed , in modo da determinare l'esito del task.

function onCompleted(args) {
	try {
		args.checkResult();
		var status = Windows.Storage.ApplicationData.current.localSettings.values["status"];
		if (status == "canceled") {
			var dialog = new Windows.UI.Popups.MessageDialog("Task annullato");
			dialog.showAsync();
		}
		else {
			var dialog = new Windows.UI.Popups.MessageDialog("Task completato");
			dialog.showAsync();
		}
	}
	catch (ex) {
		var dialog = new Windows.UI.Popups.MessageDialog("Errore durante l'esecuzione del task");
		dialog.showAsync();
	}
}

Aggiornare un background task

Un aggiornamento dell'applicazione dal Windows Store non modifica gli eventuali background task che l'applicazione ha lanciato. In pratica i task non vengono modificati dall'aggiornamento di una applicazione e vivono, come abbiamo avuto modo di spiegare nel primo articolo, in "un mondo parallelo".

Se è necessario aggiornare il codice di un background task, occorre creare una nuova versione dell'applicazione che registra un background task tramite il trigger ServiceComplete : in questo modo l'applicazione viene "avvertita" quando viene aggiornata e può deregistrare le vecchie versioni dei task e lanciare le nuove.

Il listato seguente ricerca e deregistra un task al suo avvio.

(function () {
	"use strict";
	var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
	function serviceComplete() {
		var unregisterTask = "updateGPSPosition";
		var unregTask = findTask(unregisterTask);
		if (unregTask != null) {
			unregTask.unregister(true);
		}
		close();
	}
	function findTask(taskName) {
		var taskRegistered = false;
		var background = Windows.ApplicationModel.Background;
		var iter = background.BackgroundTaskRegistration.allTasks.first();
		var hascur = iter.hasCurrent;
		while (hascur) {
			var cur = iter.current.value;
			if (cur.name === taskName) {
				return cur;
			}
			hascur = iter.moveNext();
		}
	}
	serviceComplete();
})();

Il parametro del metodo Unregister impostato a true forza la cancellazione del task, se implementato per il background task. Il metodo FindTask è semplicemente un helper che potete usare nel vostro codice per ricercare i task registrati sul sistema per assegnato nel task in fase di registrazione e restituirlo al chiamante.

L'ultima coda da fare è usare un task di tipo ServiceComplete nel codice applicativo per registrare questo task di sistema che aggiornare il task esistente (ricordatevi anche di aggiornare l'application manifest per includere il nuovo task di "servizio"):

builder.setTrigger(new background.SystemTrigger(background.SystemTriggerType.internetAvailable, false));

Debug di un background task

Il debug di un background task è praticamente impossibile se seguite l'approccio classico in quanto il task parte in background ed è slegato dall'applicazione stessa. Prima di fare un salto nel passato e adottare metodi di tracing custom, sappiate che Visual Studio 2012 e Visual Studio 2013 mettono a disposizione un debugger integrato che consente di attivare un task "a comando" e debuggarne il codice in modo semplice ed efficace.

Ricordatevi che un task basato su un timer (TimeTrigger ) o un trigger di manutenzione (MaintenanceTrigger ) può essere eseguito nei 15 minuti successivi alla registrazione in base al timer interno del Windows Runtime: per questo motivo, effettuare il debug del codice manualmente sarebbe una operazione time-consuming (dovremmo infatti aspettare che il sistema esegua il task, per poter cominciare il debug).

Per usare il debugger integrato, occorre assicurarsi di aver referenziato correttamente il background task nell'application manifest. Per il resto, non resta che inserire un breakpoint nel codice del background task per iniziare il debug.

È necessario però avviare il progetto almeno una volta per registrare il task nel sistema prima di poter usare la toolbar Debug Location per attivare il task di background. La toolbar infatti può mostrare solo i task già registrati nel sistema e in attesa di trigger. Se la toolbar non è visibile è possibile attivarla tramite il menu View > Toolbars.

La figura seguente mostra la toolbar dove è presente il background task denominato "updateGPSPositionBackgroundTask".

Ovviamente avviando il task, il debugger si fermerà sul breakpoint impostato.

Regole da ricordare per l'uso ottimale dei task

Prima di chiudere l'articolo e passare al successivo dove approfondiremo il trasferimento dei dati in background e vedremo come tenere aperto un canale di comunicazione con un servizio remoto, è importante sottolineare alcune regole per un uso ottimale dei task. Le regole principali sono le seguenti:

  • Non eccedere i limiti di CPU e rete. I task devono essere leggeri per evitare di consumare batteria e fornire una user experience migliore per le applicazioni in foreground.
  • L'applicazione deve utilizzare gli handler di completamento e progressione per informare l'utente sullo stato delle attività in background.
  • Se il task usa codice asincrono, assicurarsi di usare correttamente i deferral per evitare che il codice venga terminato prima del suo completamento.
  • Dichiarare ogni task nell'application manifest e indica la relativa Start page (ossia il percorso al file JavaScript che contiene il codice da eseguire in background), altrimenti l'applicazione non sarà in grado di registrare i task a runtime.
  • Implementare il trigger ServiceComplete per prepararsi a successivi aggiornamenti.
  • Usare tile e badge per fornire all'utente informazioni dal task in esecuzione.
  • Usare lo storage ApplicationData per condividere dati fra il task in background e l'applicazione.

Nel prossimo articolo ci divertiremo con trasferimento dei dati in background e applicazioni che necessitano di mantenere un canale di comunicazione aperto con servizi remoti.

Ti consigliamo anche