Le applicazioni Windows Store hanno un ciclo di vita che differisce notevolmente da quello delle applicazioni "tradizionali". Anzitutto possiamo avere in esecuzione in primo piano (foreground) solo una applicazione alla volta (o al più due, in modalità "snapped"): eventuali altre app sono poste in background dal sistema.
Poi bisogna considerare che le applicazioni in background possono essere sospese o addiritutta terminate da WinRT ogni volta che il sistema necessiti di liberare risorse. Perciò, se la nostra app deve svolgere un qualche tipo di attività in background (come scaricare un file o aggiornare i tile, per esempio), servirà implementare un background task.
I background task consentono quindi alle applicazioni di compiere operazioni in background, anche in processi diversi da quello principale dell'app, e soprattutto anche quando l'applicazione stessa si trova nello stato di sospensione. Un background task consente anche di informare l'utente circa l'esito delle relative operazioni tramite un toast.
D'altro canto un background task viene eseguito in un ambiente controllato dal Windows Runtime, quindi l'accesso alle risorse di sistema sarà necessariamente limitato, come vedremo. Ecco perché i compiti di un background task dovrebbero essere non troppo complessi e evitare richieste di azioni da parte dell'utente. Inoltre meglio evitare di usare background task per eseguire logica di business o calcoli troppo complessi.
Mentre in un'applicazione in XAML/C# è possibile utilizzare un componente WinMD per creare un background task, in un'app in HTML5/JavaScript il procedimento per l'esecuzione di codice in background richiede la creazione di un nuovo file JavaScript contenente la funzione che verrà eseguita al momento opportuno. Il nome del file verrà utilizzato per identificare il relativo task.
Per prima cosa, creiamo un nuovo progetto Windows Store di tipo Blank App, come illustrato nella prossima immagine:
Aggiungiamo adesso un nuovo file JavaScript (nel nostro esempio, il file è denominato updateGPSPositionBackgroundTask.js
) e includiamo il codice che verrà eseguito in background. Il prossimo snippet mostra questo punto.
(function () {
"use strict";
var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
function updateGPSPosition() {
// Codice da eseguire in background
close();
}
updateGPSPosition();
})();
La funzione sfrutta la proprietà current dell'oggetto di tipo WebUIBackgroundTaskInstance
per ottenere una reference al background task, e include il metodo updateGPSPosition
che rappresenta il codice che verrà eseguito in background non appena il task verrà attivato.
È importante ricordarsi di chiamare il metodo close
al termine della funzione, altrimenti il background task continuerà a consumare batteria, CPU e memoria anche se il codice è stato eseguito.
A questo punto è necessario definire l'evento (denominato trigger
) che scatenerà l'esecuzione del task. Quando questo evento verrà sollevato, il sistema operativo invocherà la funzione updateGPSPosition
(vedremo le diverse tipologie di trigger a disposizione nel prossimo paragrafo). Per farlo, aggiungiamo le seguenti righe di codice nel file default.js
:
var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
builder.name = "updateGPSPosition";
builder.taskEntryPoint = "js\\updateGPSPositionBackgroundTask.js";
Il codice è relativamente semplice: creiamo una nuova istanza della classe BackgroundTaskBuilder
, che rappresenta proprio un background task pronto per essere registrato presso il sistema operativo; assegniamo un nome al nostro task e indichiamo l'entry point utilizzando il path al file JavaScript contenente il codice per il task (nel nostro caso updateGPSPositionBackgroundTask.js
).
Prima di registrare il task, occorre anche definire il tipo di trigger che, una volta verificatosi, scatenerà l'esecuzione del task. In questo caso, useremo un evento di tipo MaintenanceTrigger
che verrà sollevato ogni venti minuti.
var trigger = new Windows.ApplicationModel.Background.MaintenanceTrigger(20, false);
builder.setTrigger(trigger);
Infine, non resta che registrare il task presso il sistema operativo tramite il metodo Register
della classe BackgroundTaskBuilder
.
builder.register();
Triggers 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, un 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, di tipo
SystemTriggerType
, rappresenta il tipo di trigger di sistema associate al background task, - il secondo, un Boolean denominato
oneShot
, indica a WinRT se avviare il task una volta soltanto (se impostato atrue
) ovvero ogni volta che l'evento viene sollevato.
Le seguenti righe di codice mostrano come utilizzare la classe SystemTrigger
per definire un background task che verrà eseguito ogni volta che la connessione a internet torna a essere disponibile.
var systemTrigger = new Windows.ApplicationModel.Background.SystemTrigger(Windows.ApplicationModel.Background.SystemTriggerType.internetAvailable, false);
builder.setTrigger(systemTrigger);
L'enumerazione completa dell'enum SystemTriggerType
è la seguente:
Tipo 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
, quanto quello della 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
, è unBoolean
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 Windows.ApplicationModel.Background.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, come mostrato nella prossima immagine:
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 Windows.ApplicationModel.Background.SystemCondition(Windows.ApplicationModel.Background.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 Windows.ApplicationModel.Background.SystemCondition(Windows.ApplicationModel.Background.SystemConditionType.internetAvailable));
builder.addCondition(new Windows.ApplicationModel.Background.SystemCondition(Windows.ApplicationModel.Background.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.
Nel manifest di un'applicazione Windows Store in JavaScript, come Start page deve essere indicato il path al file JavaScript che contiene il codice da eseguire in background. Anche se il manifest indica una "page", se provassimo a indicare una pagina HTML o CSS otterremmo un errore: un background task non è nient'altro che un web worker, per cui non richiede nessuna UI. Per notificare informazioni all'utente possiamo utilizzare toast, tile e badge.
Aprendo il manifest con l'editor XML, si nota che Visual Studio ha aggiunto un'apposita sezione con l'indicazione della Start page del background task e del tipo di task (systemEvent
).
<Extensions>
<Extension Category="windows.backgroundTasks"
StartPage="js\updateGPSPositionBackgroundTask.js">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
</Extensions>
C'è da tener presente che se il tipo di trigger richiede l'interazione con il Lock screen (es. PushNotificationTrigger
, TimeTrigger
, ecc.), nell'application manifest, bisognerà anche specificare il modo con cui l'app mostrerà le notifiche sul Lock screen e fornire l'immagine da utilizzare per il badge.
Enumerare i task registrati
Quando si registra un background task, è importante essere sicuri di farlo una sola volta, altrimenti potremmo vedere lo stesso task eseguito più volte. Per testare se un task è già stato registrato, possiamo usare la classe BackgroundTaskRegistration
e verificare la proprietà Value.Name
come mostrato nel seguente snippet:
var taskName = "updateGPSPosition";
var taskRegistered = false;
var taskEntryPoint = "js\\updateGPSPositionBackgroundTask.js";
var iter = Windows.ApplicationModel.Background.BackgroundTaskRegistration.allTasks.first();
while (iter.hasCurrent) {
var task = iter.current.value;
if (task.name === taskName) {
taskRegistered = true;
break;
}
iter.moveNext();
}
var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
builder.name = taskName;
builder.taskEntryPoint = taskEntryPoint;
builder.setTrigger(new Windows.ApplicationModel.Background.MaintenanceTrigger(20, false));
builder.addCondition(new Windows.ApplicationModel.Background.SystemCondition(Windows.ApplicationModel.Background.SystemConditionType.internetAvailable));
builder.addCondition(new Windows.ApplicationModel.Background.SystemCondition(Windows.ApplicationModel.Background.SystemConditionType.sessionConnected));
builder.register();
Il codice esegue un loop sui background task registrati dall'applicazione sfruttando il relativo dictionary esposto tramite la proprietà AllTasks
dell'oggetto di tipo 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, il relativo task dovrà utilizzare un deferral (la stessa tecnica utilizzata per gestire la sospensione dell'applicazione). Per ottenere un deferral, utilizziamo il metodo GetDeferral
esposto dall'oggetto di tipo WebUIBackgroundTaskInstance
che rappresenta il background task corrente.
(function () {
"use strict";
var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
function updateGPSPosition() {
var deferral = bgTaskInstance.getDeferral();
// codice da eseguire in background
deferral.complete();
close();
}
updateGPSPosition();
})();
Dopo aver richiesto una reference al deferral, possiamo utilizzare il pattern asincrono per eseguire del codice in background e, al termine delle relative operazioni, invocare il metodo Complete
sul deferral per segnalare al sistema che l'operazione è conclusa. È importante ricordarsi 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 esamineremo con esempi pratici alcune casistiche legate ai background task.