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

Scambiare dati in background (XAML/C#)

Link copiato negli appunti

Negli articoli precedenti abbiamo visto come definire a avviare task in background. In questo articolo vedremo come scambiare dati in background ovvero trasferire file e risorse da e verso servizi remoti.

Trasferire dati in background

Uno degli scenari più frequenti di utilizzo di un background task è quello di dover compiere operazioni di download o upload di una risorsa web.

A causa del particolare ciclo di vita di un'applicazione Windows Store, se fosse l'applicazione principale ad effettuare il download o l'upload, si correrebbe il rischio di sospensione o perfino di terminazione durante l'esecuzione delle operazioni: nel caso in cui, ad esempio, l'utente portasse l'app in background e passasse ad altro (ipotesi tutt'altro che rara, soprattutto se lo costringessimo ad attendere il completamento di lunghe operazioni di download). In casi come questo, anche i nostri trasferimenti di dati verrebbero sospesi (o terminati), poiché un'app sospesa non dispone di CPU, né può accedere al filesystem.

Per questa ragione, il namespace Windows.Networking.BackgroundTransfer mette a disposizione una serie di classi che permettono di evitare questo problema eseguendo le operazioni di download/upload di una risorsa in background.

Questo meccanismo di trasferimento in background supporta i protocolli HTTP e HTTPS sia per le operazioni di download che quelle di upload, mentre il protocollo FTP è supportato solo per il download.

Queste API sono state inoltre pensate per il trasferimento di file di grandi dimensioni, anche notevoli, come musica, immagini di alta qualità o video (sebbene le API funzionino anche per file di piccole dimensioni, in questo caso Microsoft suggerisce di usare le tradizionali classi HttpClient).

Le due classi responsabili delle operazioni di download e di upload sono, rispettivamente:

  • BackgroundDownloader
  • BackgroundUploader

Tramite queste classi è possibile avviare operazioni di downoad/upload concorrenti (purché mediante URI differenti), dato che WinRT gestisce ciascuna operazione separatamente. Durante lo svolgimento delle operazioni, l'applicazione può ricevere eventi per aggiornare la user interface (nel caso in cui l'app sia ancora in foreground), ed è anche possibile interrompere, riprendere o cancellare l'operazione. Queste API supportano inoltre l'uso di credenziali, cookie e header HTTP, in modo da poter utilizzare molteplici meccanismi di sicurezza.

Tutte le operazioni sono gestire direttamente da WinRT, in modo da ottimizzare il consumo di risorse (in particolare delle batterie) e della bandaIl seguente codice mostra un esempio di utilizzo della classe BackgroundDownloader per avviare il download di un filmato al click di un ipotetico pulsante, per poi salvarlo nella cartella Videos dell'utente:

private async void Download_Click(object sender, RoutedEventArgs e)
{
	try
	{
		var fileName = "samplevideo.wmv";
		Uri source = new Uri("http://www.contoso.com/videos/" + fileName);
		var targetFile = await KnownFolders.VideosLibrary.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);
		var downloader = new BackgroundDownloader();
		var downloadOperation = downloader.CreateDownload(source, targetFile);
		await downloadOperation.StartAsync();
	}
	catch (Exception ex)
	{
		// Gestire l'eccezione
	}
}

Prima di eseguire il codice, tuttavia è necessario modificare l'application manifest per aggiungere una o più delle seguenti "capability":

Capability Descrizione
Internet (Client) accesso in uscita a internet e reti in aree pubbliche (pub, negozi, aeroporti, stazioni)
Internet (Client & Server) l'app può effettuare e ricevere richieste in aree pubbliche
Private Networks l'app può effettuare e ricevere richieste solo in reti private (home e work)

La prossima figura mostra l'application manifest con le capability richieste per l'esecuzione in background di operazioni di download/upload. Da notare che è stata aggiunta anche la capability Videos Library, dato che l'app ha bisogno di accedere alla relativa cartella dell'utente per salvare il video scaricato da Internet.

Il codice sopra riportato è piuttosto semplice: una volta creata l'URI per la risorsa da scaricare, il metodo StorageFolder.CreateFileAsync crea un file nella libreria Videos dell'utente utilizzando il pattern async/await.

La classe BackgroundDownloader espone un metodo CreateDownload che permette di creare un'operazione di download asincrona, rappresentata da un oggetto di tipo DownloadOperation. Questo metodo, in uno degli overload disponibili, accetta come parametri l'URI che identifica la risorsa e un oggetto di tipo IStorageFile che rappresenta il file che verrà usato per salvarla. Per avviare l'operazione, è quindi sufficiente invocare il metodo StartAsync della classe BackgroundDownloader.

La classe BackgroundDownloader permette anche di specificare la politica da seguire in caso di reti con traffico dati a pagamento ("costed newtork"). Il seguente snippet ne mostra un esempio:

var downloader = new BackgroundDownloader();
downloader.CostPolicy = BackgroundTransferCostPolicy.UnrestrictedOnly;

In questo modo, nel caso di una connessione a una rete a pagamento eventuali operazioni di trasferimento verranno sospese automaticamente, per essere poi riprese non appena il device verrà connesso a una rete gratuita.

È anche possibile differenziare la politica dei costi da seguire per le single operazioni, sfruttando questa volta la proprietà CostPolicy esposta dalla classe DownloadOperation che, come si è detto, rappresenta una specifica operazione di trasferimento.

var downloadOperation = downloader.CreateDownload(source, targetFile);
downloadOperation.CostPolicy = BackgroundTransferCostPolicy.Always;

La classe DownloadOperation espone anche due metodi, Pause e Resume, che permettono, rispettivamente, di interrompere e riprendere l'operazione di trasferimento.

Per tenere traccia del progredire delle operazioni di trasferimento, è possibile sfruttare il metodo StartAsync, in modo che restituisca un Task e passando come parametro una callback di tipo System.Progress<T>. Il seguente listato mostra una versione modificata del codice illustrato in precedenza.

private async void Download_Click(object sender, RoutedEventArgs e)
{
	try
	{
		var fileName = " samplevideo.wmv";
		Uri source = new Uri("http://www.contoso.com/videos/" + fileName);
		var targetFile = await KnownFolders.VideosLibrary.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);
		var downloader = new BackgroundDownloader();
		var downloadOperation = downloader.CreateDownload(source, targetFile);
		var progressCallback = new Progress(OnDownloadProgress);
		await downloadOperation.StartAsync().AsTask(progressCallback);
	}
	catch (Exception ex)
	{
		// Gestire l'eccezione
	}
}
private void OnDownloadProgress(DownloadOperation operation)
{
	tbProgress.Text = String.Format("Trasferiti {0} byte su {1}.",
	operation.Progress.BytesReceived,
	operation.Progress.TotalBytesToReceive);
}

Come si vede, il metodo StartAsync restituisce un oggetto di tipo:

IAsyncOperationWithProgress<DownloadOperation, DownloadOperation>

che viene trasformato in un Task utilizzando il metodo AsTask. Questo metodo riceve una istanza di Progress<T> che rappresenta la callback.

In questo modo, la callback riceverà un oggetto di tipo DownloadOperation e, tramite la relativa proprietà Progress (di tipo BackgroundDownloadProgress), sarà possibile conoscere lo stato di avanzamento dell'operazione.

In particolare, le proprietà esposte dalla classe BackgroundDownloadProgress sono le seguenti:

Proprietà Descrizione
BytesReceived il numero di byte ricevuti (non sono computati gli header della risposta)
HasResponseChanged se true, indica che la risposta è cambiata a seguito di una nuova richiesta
HasRestarted se true, indica che l'operazione di trasferimento è stata interrotta e che il server non supporta il resume del download, per cui la nuova operazione di download dovrà ripartire dall'inizio
Status questa proprietà, di tipo BackgroundTransferStatus, indica lo stato corrente dell'operazione; può assumere uno dei seguenti valori dai nomi auto-esplicativi: Idle, Running, PausedByApplication, PausedCostedNetwork, PausedNoNetwork, Completed, Canceled, Error
TotalBytesToReceive il numero totale di byte da scaricare (se la dimensione è ignota, il valore della proprietà è impostato a zero)

Dal momento che WinRT può sospendere o terminare l'applicazione in qualunque momento, durante il lancio successivo dell'app da parte dell'utente è importante re-impostare la callback che permette di monitorare lo stato di avanzamento del trasferimento. Per sapere quali sono le operazioni di download pendenti in un certo momento, è possibile usare il metodo GetCurrentDownloadAsync della classe BackgroundDownloader. Il prossimo snippet mostra come fare.

IReadOnlyList<DownloadOperation> downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
if (downloads.Count > 0)
{
	List<Task> tasks = new List<Task>();
	foreach (DownloadOperation download in downloads)
	{
		var progressCallback = new Progress<DownloadOperation>(OnDownloadProgress);
		await download.AttachAsync().AsTask(progressCallback);
	}
}

Il codice enumera tutte le operazioni di download correnti e re-imposta la callback per monitorare il progresso di ciascuna di esse tramite il metodo AttachAsync. Questo metodo permette a un'app di riprendere operazioni di download iniziate durante una precedente esecuzione della medesima app.

Un'altra cosa da considerare riguarda la politica di time-out seguita da WinRT. Quando viene creata una nuova richiesta di connessione, il tempo a disposizione per stabilire una connessione è di cinque minuti, trascorsi i quali la connessione viene abortita. Dopo aver stabilito la connessione, ogni richiesta HTTP che non riceve una risposta nei due minuti successivi verrà a sua volta abortita.

Finora abbiamo visto il funzionamento della classe BackgroundDownloader, ma gli stessi concetti si applicano anche all'upload di un file tramite la classe BackgroundUploader. Anche questa classe è infatti pensata per file di grandi dimensioni, come immagini di alta qualità, video e musica (mentre per file di ridotte dimensioni è sempre possibile ricorrere alla tradizionale classe HttpClient).

Il prossimo snippet mostra un esempio di utilizzo della classe BackgroundUploader in cui sono evidenti le analogie con quanto visto per il download:

private async void Upload_Click(object sender, RoutedEventArgs e)
{
	try
	{
		var fileName = "samplevideo.vmw";
		Uri source = new Uri("http://www.contoso.com/videos");
		var file = await KnownFolders.VideosLibrary.GetFileAsync(fileName);
		var uploader = new BackgroundUploader();
		uploader.CostPolicy = BackgroundTransferCostPolicy.UnrestrictedOnly;
		uploader.Method = "POST";
		var uploadOperation = uploader.CreateUpload(source, file);
		var progressCallback = new Progress(OnUploadProgress);
		await uploadOperation.StartAsync().AsTask(progressCallback);
	}
	catch (Exception ex)
	{
		// gestire eccezione
	}
}
private void OnUploadProgress(UploadOperation operation)
{
	tbProgress.Text = String.Format("Trasferiti {0} byte su {1}. Status: {2}",
			operation.Progress.BytesSent,
			operation.Progress.TotalBytesToSend,
			operation.Progress.Status);
}

In alternativa, possiamo utilizzare il metodo CreateUploadFromStreamAsync, il quale restituisce, una volta completata l'operazione, un oggetto di tipo UploadOperation con l'URI indicata e lo stream. Il prossimo snippet mostra un possibile utilizzo di questa classe:

private async void Upload_Click(object sender, RoutedEventArgs e)
{
	try
	{
		var fileName = "samplevideo.vmw";
		Uri source = new Uri("http://www.contoso.com/videos");
		var uploader = new BackgroundUploader();
		uploader.CostPolicy = BackgroundTransferCostPolicy.UnrestrictedOnly;
		uploader.Method = "POST";
		using (var fileStream = awaitKnownFolders.VideosLibrary.OpenStreamForReadAsync(fileName))
		{
			using (IInputStream stream = fileStream.AsInputStream())
			{
				var uploadOperation = await uploader.CreateUploadFromStreamAsync(source, stream);
				var progressCallback = newProgress<UploadOperation>(OnUploadProgress);
				await uploadOperation.StartAsync().AsTask(progressCallback);
			}
		}
	}
	catch (Exception ex)
	{
		// gestire l'eccezione
	}
}

È anche possibile usare il metodo CreateUploadAsync per creare operazioni asincrone che, una volta completate, restituiscono un oggetto di tipo UploadOperation (per ulteriori approfondimenti su questo metodo si rinvia alla documentazione di MSDN).

Infine, entrambe le classi BackgroundDownloader e BackgroundUploader espongono le proprietà ProxyCredential, per l'autenticazione presso un proxy, e ServerCredential, per l'autenticazione presso un server. È anche possibile usare il metodo SetRequestHeader per specificare gli header HTTP.

Mantenere aperti i canali di comunicazione

Per applicazioni che richiedono di lavorare in background, come applicazioni Voice over Internet Protocol (VoIP), instant messaging (IM) ed email, WinRT mette a disposizione una esperienza utente di tipo "always-connected". In pratica, un'app che necessita di una connessione "duratura" verso un server remoto è in grado di funzionare anche in caso di sospensione dell'app da parte di WinRT.

Mantenere un canale di comunicazione aperto è importante per quelle applicazioni che inviano o ricevono dati da endpoint remoti, o che devono attendere il completamento di lunghe operazioni lato server. In genere, questo tipo di applicazioni si trova dietro un proxy, un firewall o un device Network Address Translation (NAT). Questi componenti hardware mantengono aperta la connessione solo se i due endpoint continuano a scambiare dati, altrimenti la connessione viene chiusa.

Per evitare la chiusura della connessione tra il server e il client, l'applicazione può essere configurata per usare una connessione "keep-alive" (che prevede l'invio di messaggi a intervalli regolari, in modo da prolungare la durata della connessione).

Questa strategia era di semplice implementazione nelle precedenti versioni di Windows, in quanto l'applicazione rimaneva in esecuzione fino a quando l'utente non decideva di chiuderla. In Windows 8, al contrario, l'app può essere sospesa o addirittura terminata dal sistema, non appena in background, con la conseguente chiusura di ogni connessione eventualmente aperta.

Per mantenere la connessione aperta, un'applicazione Windows Store ha bisogno di sfruttare il Lock screen. È importante ricordare che ogni utente può avere fino a un massimo di 7 app contemporaneamente nel Lock screen.

Bisogna ricordare che l'app ha bisogno di ricevere notifiche (ad esempio per aggiornare un live tile o un badge, eseguire del codice in risposta a una notifica, mostrare le notifiche all'utente, ecc.), si può sfruttare il Windows Push Notification Service (WNS), un servizio cloud ospitato da Microsoft per Windows 8.

Se invece abbiamo bisogno di mantenere la socket aperta con un server o endpoint remoto, la classe ControlChannelTrigger del namespace Windows.Networking.Sockets permette di mantenere una connessione persistente (questa classe non è disponibile in una Windows Store app scritta in HTML5/JavaScript).

Si può utilizzare questa funzionalità ogni volta che non è possibile sfruttare il Windows Push Notification Service (ad esempio, un'applicazione di mail che usa un server POP3 non può sfruttare il WNS, poiché il server non implementa questo servizio e non può inviare messaggi ai clienti POP3).

La classe ControlChannelTrigger può essere usata tramite istanze delle seguenti classi: MessageWebSocket, StreamWebSocket, StreamSocket, HttpClient, HttpClientHander, e le relative classi del namespace System.Net.Http di .NET Framework 4.5.

Il beneficio maggiore nell'uso di un "network trigger" (che come si è detto consente di ricevere dati e di mantenere la connessione aperta) è dato dalla compatibilità con protocolli client/server esistenti e garantisce la consegna del messaggio. Lo svantaggio è dato da una maggiore complessità rispetto a WNS, e il numero massimo di network trigger utilizzabili da un'app è di cinque (almeno per adesso).

Il WNS presenta anche altre limitazioni (ad esempio, non è disponibile se il device è connesso a una rete locale che non ha accesso a Internet, o è nascosto dietro un firewall; inoltre, il payload massimo per una push notification è di 5 kilobyte).

Esistono due tipi di network trigger:

  • Push notification network trigger
    permette a un'app Windows Store di processare pacchetti in arrivo su una socket TCP già stabilita, anche se l'applicazione è sospesa. Questa socket rappresenta il canale di controllo tra l'applicazione e l'endpoint remote, ed è creata dall'applicazione per eseguire un background task alla ricezione di un pacchetto da parte dell'applicazione. In pratica, il control channel è dato da una connessione TCP/IP tenuta in vita da WinRT anche se l'app è stata messa in background e conseguentemente sospesa.
  • Keep-alive network trigger
    permette a un'app, anche sospesa, di inviare pacchetti keep-alive a un servizio o endpoint remote, evitando in questo modo la chiusura della connessione.

Un network trigger (push notification o keep-alive) supporta due tipi di risorse possibili:

  • Slot hardware
    consente all'app di usare notifiche di rete in background anche in modalità basso consumo (questo particolare stato è denominato "connected standby" e, in Windows 8, presenta caratteristiche particolari, diversi dai tradizionali stati di "sleep" e "hibernate" già noti agli utenti delle versioni precedenti di Windows; per un approfondimento sul punto si rinvia alla documentazione MSDN).
  • Slot software
    consente all'app di usare notifiche di rete in background, ma non in modalità connected standby.

Come vedremo più avanti, una delle opzioni da specificare durante la registrazione di un network trigger è il tipo di risorsa: slot hardware o slot software. La scelta del tipo di risorsa mette l'app in condizione di ricevere dati anche se il dispositivo è in modalità connected standby. È importante ricordare che solo i sistemi con dispositivi di rete che supportano questa particolare modalità avranno uno "slot hardware".

Come abbiamo già detto, per usare un network trigger l'applicazione deve essere aggiunta al Lock screen. È dunque necessario aggiungere il logo per il badge (ed eventualmente per il tile) nell'application manifest e, successivamente, chiedere all'utente il permesso di aggiungere l'app al Lock screen (ricordiamo anche di gestire l'eventualità che l'app sia rimossa dal Lock screen).

La prossima immagine mostra l'application manifest modificato per includere la funzionalità di Lock screen.

Il passo successivo consiste nel dichiarare il background task che vogliamo utilizzare, come abbiamo già visto nell'articolo introduttivo ai background task, indicando il file eseguibile che contiene il task e il corrispondente entry point.

Il task deve essere di tipo Control Channel; per questo tipo di task, l'eseguibile è rappresentato dall'applicazione medesima, mentre la DLL che implementa il task deve essere referenziata come Windows Runtime Component (WinMD). La prossima immagine mostra l'application manifest con la relativa dichiarazione:

Per richiedere all'utente del permesso di accedere al Lock screen, è necessario invocare il metodo RequestAccessAsync della classe:

Windows.ApplicationModel.Background.BackgroundExecutionManager

La chiamata a questo metodo mostra all'utente un dialog, tramite il quale l'utente può decidere di concedere o meno il permesso. È importante sottolineare come questo dialog venga mostrato soltanto la prima volta: questo vuol dire che, se l'utente nega il permesso, l'applicazione non potrà più chiedere all'utente di essere aggiunta al Lock screen nella sezione Personalize dei PC Settings.

Il codice che segue mostra come richiedere il permesso per il Lock screen:

private Boolean _lockScreenPermitted = false;
private async void LockScreenEnabling_Click(object sender, RoutedEventArgs e)
{
	BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
	switch (status)
	{
		case BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity:
			this._lockScreenPermitted = true;
			break;
		case BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity:
			_lockScreenPermitted = true;
			break;
		case BackgroundAccessStatus.Denied:
			break;
	}
}

L'enum BackgroundAccessStatus permette di conoscere la scelta effettuata dall'utente. L'enum può assumere uno dei seguenti valori, a seconda della risposta dell'utente e del tipo di device:

  • Unspecified: l'utente non ha prestato né negato il suo permesso (ad esempio perché ha chiuso l'applicazione prima di effettuare una scelta). In questo caso, al successivo avvio l'app potrà mostrare nuovamente il dialog per richiedere il permesso.
  • AllowedWithAlwaysOnRealTimeConnectivity: l'utente ha prestato il suo consenso all'aggiunta dell'app al Lock screen. L'app è dunque aggiunta al Lock screen, può usare background task e, se necessario, può usare il Real-Time Connectivity broker. Questo significa che l'app può funzionare anche se il device si trova in modalità connected-standby (slot hardware).
  • AllowedMayUseActiveRealTimeConnectivity: l'utente ha prestato il suo consenso all'aggiunta dell'app al Lock screen. L'app è dunque aggiunta al Lock screen e può usare background task; tuttavia non potrà usufruire del Real-Time Connectivity broker, con la conseguenza che l'app potrà non funzionare se il device si trova in modalità connected-standby (slot software).

Dopo che l'app è stata aggiunta al Lock screen, dovrebbe essere visibile nella sezione Personalize dei PC Settings. Ricordati di gestire a livello applicativo il diniego e la revoca del permesso da parte dell'utente.

Una volta che l'app è stata aggiunta al Lock screen, i passaggi successivi sono i seguenti:

  1. Creare un control channel
  2. Aprire una connessione
  3. Associare la connessione al control channel
  4. Connettere la socket al server o endpoint remoti
  5. Stabilire una connessione per il trasporto verso il server o l'endpoint remoti

Occorre creare il canale da associare alla connessione in modo che la connessione stessa resti aperta fino a che non decidiamo di chiudere il control channel. Una volta aperta la connessione col server, occorre sincronizzare il meccanismo di trasporto creato dall'applicazione con i livelli sottostanti del sistema operativo utilizzando una specifica API, come mostrato nel seguente snippet.

private Windows.Networking.Sockets.ControlChannelTrigger channel;
private void CreateControlChannel_Click(object sender, RoutedEventArgs e)
{
	ControlChannelTriggerStatus status;
	// 1: Crea il control channel.
	this.channel = new Windows.Networking.Sockets.ControlChannelTrigger(
		"ch01", // ID del control channel
		 20,    // Intervallo di keep-alive (in minuti) lato server
		ControlChannelTriggerResourceType.RequestHardwareSlot); //Richiesta di uno slot hardware
	// Crea il network trigger
	BackgroundTaskBuilder controlChannelBuilder = new BackgroundTaskBuilder();
	controlChannelBuilder.Name = "ReceivePacketTaskChannelOne";
	controlChannelBuilder.TaskEntryPoint = "ControlChannellTriggerTask.ReceiveTask";
	controlChannelBuilder.SetTrigger(channel.PushNotificationTrigger);
	controlChannelBuilder.Register();
	// 2: Crea una socket
		// codice omesso per brevità
	// 3: Associa la connessione al control channel
	channel.UsingTransport(socket);
	// 4: Connette la socket
		// codice omesso per brevità
	// 5: Sincronizza con i layer dello stack
		status = channel.WaitForPushEnabled();
}

Per prima cosa, il codice istanzia un oggetto di tipo ControlChannelTrigger passando come parametri una stringa che identifica il canale, l'intervallo keep-alive lato server, assieme alla richiesta di riservare uno slot hardware (per poter funzionare anche in connected standby) identificativo.

A proposito degli intervalli keep-alive, ci sono alcune cose che dovrebbero essere considerate quando si usa la classe ControlChannelTrigger.

  • Intervallo keep-alive lato server
    questo intervallo è espresso in minuti e deve essere impostato nel costruttore della classe (puoi verificare in ogni momento il valore di questo parametro tramite la proprietà ServerKeepAliveIntervalInMinutes). Il valore dell'intervallo dipende dalle impostazioni del server: ad esempio, se il tempo di timeout del server è di 20 minuti, trascorsi i quali la connessione viene chiusa, puoi impostare questa proprietà a 18 minuti per evitare la chiusura. Tieni presente che l'intervallo minimo è di 15 minuti; impostare un valore inferiore causa il sollevamento di un'eccezione.
  • Intervallo keep-alive della rete: questo intervallo viene usato dallo stack TCP con proxy, gateway, NAT e altri intermediari di rete per mantenere aperta la connessione. Il valore di questo intervallo è determinato automaticamente da Windows, sulla base di una serie di misurazioni euristiche.

Il sistema operativo tiene conto di entrambi questi valori e sceglie una velocità ottimale alla quale inviare i messaggi keep-alive per una determinata app in una determinata rete, in modo da preservare la connessione tra il client e l'endpoint remoto.

Il passo successivo consiste nel creare un network trigger di tipo PushNotificationTrigger tramite il metodo SetTrigger della classe BackgroundTaskBuilder già analizzata nei precedenti articoli dedicati ai background task.

Dopodiché, occorre creare una socket (ad esempio, una StreamSocket) e associare il canale di trasporto con il control channel tramite il metodo UsingTransport. È da notare come questo metodo debba essere chiamato dopo che la socket è stata creata, ma prima che la connessione sia stabilita.

L'ultimo passaggio consiste nell'invocare il metodo WaitForPushEnabled. Nonostante il suo nome, questo metodo non ha nulla a che fare con il WNS, ma permette di registrare lo slot hardware o software con i sottostanti layer dello stack che gestiranno i dati in arrivo.

L'ultima cosa da fare consiste nell'implementare il background task che provvederà a compiere le operazioni necessarie in background all'arrivo di nuovi dati, come aggiornare un tile o inviare un toast. Il seguente snippet mostra un esempio di background task:

public sealed class ReceiveTask : IBackgroundTask
{
	public void Run(IBackgroundTaskInstance taskInstance)
	{
		var channelEventArgs = (IControlChannelTriggerEventDetails)taskInstance.TriggerDetails;
		var channel = channelEventArgs.ControlChannelTrigger;
		string channelId = channel.ControlChannelTriggerId;
		// Invia un toast o aggiorna un tile
		channel.FlushTransport();
	}
}

La proprietà TriggerDetails, di tipo IControlChannelTriggerEventDetails, fornisce le informazioni necessarie per accedere alla notifica grezza ed espone la proprietà ControlChannelTriggerId che l'applicazione può usare per identificare le diverse istanze di control channel.

Come abbiamo visto in questi tre articoli in sequenza, ci sono molte possibilità di utilizzo di un background task: possiamo semplicemente eseguire qualche operazione locale in modo indipendente dall'applicazione, così come trasferire dati da e verso risorse remote.

Ti consigliamo anche