Um einzelne Push Notifications an die gängigen Betriebssysteme für mobile Endgeräte verschicken zu können, bot die Windows Azure Plattform bislang die Mobile Services als Lösung an.
Seit Ende Januar ist ein weiterer Dienst hinzugekommen, mit dem man das großflächigen Versenden von Push-Benachrichtigungen realisieren kann.
Die neuen Notification Hubs für Windows Azure, die vorerst in einer Preview zur Verfügung stehen, bieten eine hochskalierbare Infrastruktur zum großflächigen Versenden von Push-Notifications auf mobile Endgeräte.
Der ersten Teil dieser kleinen Blog Post Reihe befasst sich mit den Grundlagen, sowie der Anbindung einer Windows Store App.
Push Notification Services
Für alle mobile Endgeräte, funktionieren die Push-Notification-Services nach einem ähnlichen Prinzip:
Bevor die öffentliche Infrastruktur einer App Push-Benachrichtigungen verschicken kann, muss diese bei den jeweiligen Push-Notification-Services registriert werden.
Daraufhin können Apps sich für Push-Benachrichtigungen anmelden und diese anschließend erhalten:
-
Dazu fordert die App bei dem im Betriebssystem eingebetteten Push-Notification-Client eine Art Token bzw. ID an.
Bei Windows Store Apps ist dies eine Channel URI. -
Der lokale Push-Notification-Client fordert diesen Token beim Push-Notification-Service an …
Bei Windows Store Apps heißt dieser Windows Push Notification Services (WNS).
Bei Apple wird dieser als Apple Push Notification Service (APNS) bezeichnet. - … und gibt die Antwort an die App zurück.
- Die App übermittelt den Token an die eigene, serverseitige Infrastruktur, welche diese für den Client abspeichert.
- Wenn jetzt eine App benachrichtigt werden soll, wird eine Nachricht inkl. des Empfänger-Tokens an den Push-Notification-Service gesendet …
- … welcher diese an den entsprechenden Push-Notification-Client weiterleitet.
Soweit zum klassischen Weg.
Aber wie sieht das Ganze jetzt mit den Windows Azure Service Bus Notification Hubs aus?
Erstellen eines Notification Hubs
Über "Create > App Services > Service Bus Notification Hub > Quick Create" kann im Windows Azure Management Portal ein neuer Notification Hub erstellt werden.
Neben der üblichen Region- und Subscription-Auswahl, besteht außerdem die Möglichkeit einen neuen Service Bus Namespace erstellen zu lassen oder einen bereits existierenden auszuwählen:
Wenige Minuten später ist der Notification Hub erstellt und kann im Configure-Bereich mit den Push-Notification-Services verbunden werden:
Windows Store App reservieren
Um an die Package SID und das Client Secret zu gelangen, muss man zuerst im Dev Center für Windows Store Apps eine neue App anlegen (Dashboard > App übermitteln):
Anschließend gelangt man über "Erweiterte Features" in den Bereich "Info zu Pushbenachrichtigungen und Live Connect-Diensten":
Dort findet man unter "Authentifizieren des Diensts" die gewünschten Informationen:
Nachdem diese Informationen in den Configure-Bereich des Notification Hubs hinzugefügt und gespeichert wurden, können Push-Notifications an die Windows Store App versendet werden.
Die Windows Store App anpassen
Damit die Windows Store Apps die Push-Benachrichtigungen auch erhalten können, müssen diese eine Channel-URI bei den Windows Push Notification Services (WNS) anfordern und diese an den Notification Hub weiterleiten.
Um beispielsweise Toast-Benachrichtigungen erhalten zu können, müssen diese zuerst im Package.appmanifest freigeschaltet werden:
Anschließend muss eine Referenz zum Service Bus WinRT Managed SDK (Microsoft.WindowsAzure.Messaging.Managed) dem Projekt hinzugefügt werden.
Hinweis:
Das Service Bus WinRT Managed SDK kann unter diesem Link heruntergeladen werden.
Für die Verbindung zum Notification Hub sollte man folgenden Codezeilen in der App.xaml.cs hinzufügen:
sealed partial class App { private readonly NotificationHub _notificationHub; /// <summary> /// Initializes the singleton application object. /// This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// </summary> public App() { var cn = ConnectionString.CreateUsingSharedAccessSecretWithListenAccess( "sb://<Service Bus Namespace>.servicebus.windows.net/", "<Passwort des DefaultListenSharedAccessSignature Kontos>"); _notificationHub = new NotificationHub("myhub", cn); this.InitializeComponent(); this.Suspending += OnSuspending; }
Das Passwort des DefaultListenSharedAccessSignature Kontos findet man im Notification Hub, wenn man auf "VIEW SAS KEY" in der unteren Leiste des Management Portals klickt:
Mit Hilfe dieses Verbindungsobjektes, lässt sich nun folgender Code ausführen:
private async Task InitializeNotificationsAsync() { try { // Get latest Channel-URI and send it to the Notification Hub await _notificationHub.RefreshRegistrationsAsync(); // Register for native Push-Notifications if (!await _notificationHub.RegistrationExistsForApplicationAsync()) { await _notificationHub.CreateRegistrationForApplicationAsync(); } } catch (Exception ex) { new MessageDialog( String.Format("An error occured: {0}", ex.Message), "Notification Hub Registration").ShowAsync(); } }
Diese Methode stellt 2 Dinge bereit:
- Der Notification Hub wird mit einer aktuellen Channel-URI aktualisiert.
- Die App kann jetzt native Push-Benachrichtigungen erhalten.
Vorlagen für Push-Benachrichtigungen
Bei der Verwendung von nativen Push-Benachrichtigungen besteht allerdings ein Problem:
Wie unterstütze ich Windows Store- UND iOS-Apps?
Hierzu hat sich das Service Bus Team etwas besonderes einfallen lassen.
Jede App kann bei der Registrierung am Notification Hub Vorlagen mitsenden, die für das Versenden der Nachrichten verwendet werden sollen.
Bei den Notification Hub-Vorlagen lassen sich dabei Platzhalter definieren, die später mit Hilfe von Schlüssel/Wert-Paaren gefüllt werden:
Expression | Description |
---|---|
$(prop) |
Platzhalter mit dem Schlüssel "prop". |
$(prop, n) |
Wie bei $(prop), allerdings mit einer Maximallänge von n Zeichen. |
.(prop, n) |
Wie bei $(prop, n), allerdings werden drei Punkte angehängt wenn der Text gekürzt werden muss. |
%(prop) |
Wie bei $(prop), allerdings ist der Text URI encoded. |
#(prop) |
Wird bei den iOS Vorlagen benutzt, um den Zahlenwerte einzusetzen. |
$body |
Setzt den "body" der Service Bus Nachricht ein. |
expr1 + expr2 |
Mit dem + Operator können Strings miteinander verbunden werden. |
Beispielsweise könnte aus der Vorlage für eine Toast-Nachricht mit einem Textblock …
<toast> <visual> <binding template="ToastText01"> <text id="1">value</text> </binding> </visual> </toast>
… folgende Notification Hub-Vorlage gemacht werden:
<toast> <visual> <binding template="ToastText01"> <text id="1">$(msg)</text> </binding> </visual> </toast>
Der Code der InitializeNotificationsAsync Methode wurde dann folgendermaßen aussehen:
private async Task InitializeNotificationsAsync() { try { await _notificationHub.RefreshRegistrationsAsync(); if (!await _notificationHub.RegistrationExistsForApplicationAsync("toast")) { await _notificationHub.CreateTemplateRegistrationForApplicationAsync( NotificationXmlBuilder.CreateToastText01Xml("$(msg)"), "toast"); } } catch (Exception ex) { new MessageDialog( String.Format("An error occured: {0}", ex.Message), "Notification Hub Registration").ShowAsync(); } }
Nachdem die InitializeNotificationsAsync Methode erstellt wurde, muss diese zu den OnLaunched und OnActivated Methoden hinzugefügt werden:
protected async override void OnLaunched(LaunchActivatedEventArgs args) { await InitializeNotificationsAsync(); // ... } protected async override void OnActivated(IActivatedEventArgs args) { base.OnActivated(args); await InitializeNotificationsAsync(); }
Verschicken von Nachrichten
Damit jetzt Push-Benachrichtigungen verschickt werden, muss in ein beliebiges Projekt, wie beispielsweise eine Konsolenapplikation oder auch eine Windows Azure Web Role, das NuGet Paket ServiceBus.Preview hinzugefügt und folgende Methode aufgerufen werden:
public void SendMessage(string message) { var connectionString = ServiceBusConnectionStringBuilder .CreateUsingSharedAccessSecretWithFullAccess("<Service Bus Namespace>", "<Passwort des DefaultFullSharedAccessSignature Kontos>"); var hubClient = NotificationHubClient .CreateClientFromConnectionString(connectionString, "myhub"); hubClient.SendTemplateNotification(new Dictionary { {"msg", message} }); }
Mehrere Vorlagen registrieren
In den Apps können auch mehrere Vorlagen registriert werden, um beispielsweise mit nur einer Notification Hub-Nachricht gleichzeitig eine Toast-Nachricht zu erzeugen und das Live-Tile zu aktualisieren:
private async Task InitializeNotificationsAsync() { try { await _notificationHub.RefreshRegistrationsAsync(); if (!await _notificationHub.RegistrationExistsForApplicationAsync("toast")) { await _notificationHub.CreateTemplateRegistrationForApplicationAsync( NotificationXmlBuilder.CreateToastText01Xml("$(msg)"), "toast"); } if (!await _notificationHub.RegistrationExistsForApplicationAsync("tile")) { await _notificationHub.CreateTemplateRegistrationForApplicationAsync( BuildTileTemplate(), "tile"); } } catch (Exception ex) { new MessageDialog( String.Format("An error occured: {0}", ex.Message), "Notification Hub Registration").ShowAsync(); } } private static XmlDocument BuildTileTemplate() { var tileXml = NotificationXmlBuilder.CreateTileWideText04Xml("$(msg)"); var visualNode = tileXml.SelectSingleNode("//visual") as XmlElement; if (visualNode != null) { var squareTileXml = NotificationXmlBuilder.CreateTileSquareText04Xml("$(msg)"); var bindingNode = tileXml.ImportNode( squareTileXml.GetElementsByTagName("binding").Item(0), true); visualNode.AppendChild(bindingNode); } return tileXml; }
Gezieltes Versenden von Nachrichten
Wenn gezielt kleinere Gruppen oder auch einzelne Empfänger angesprochen werden sollen, muss man bei der Registrierung der Vorlagen eine Liste von Schlagwörtern (Tags) mit angeben.
Dies könnte beispielsweise wie folgt aussehen, um
- mit dem "All"-Tag beide Nachrichten an alle Empfänger zu senden
- mit den "Toast"- bzw. "Tile"-Tags nur Toastnachrichten oder nur Live-Tile Updates zu verschicken
- mit der App Specific Hardware ID (ASHWID) ein einzelnes Gerät ansprechen zu können
- mit der UserID alle Geräte eines einzelnen Benutzers erreichen zu können
private async Task InitializeNotificationsAsync() { try { await _notificationHub.RefreshRegistrationsAsync(); var ashwid = GetAppSpecificHardwareId(); var userid = GetUserId(); if (!await _notificationHub.RegistrationExistsForApplicationAsync("toast")) { await _notificationHub.CreateTemplateRegistrationForApplicationAsync( NotificationXmlBuilder.CreateToastText01Xml("$(msg)"), "toast", new[] { "All", "Toast", ashwid, userid }); } if (!await _notificationHub.RegistrationExistsForApplicationAsync("tile")) { await _notificationHub.CreateTemplateRegistrationForApplicationAsync( BuildTileTemplate(), "tile", new[] { "All", "Tile", ashwid, userid }); } } catch (Exception ex) { new MessageDialog( String.Format("An error occured: {0}", ex.Message), "Notification Hub Registration").ShowAsync(); } } private static string GetAppSpecificHardwareId() { var token = Windows.System.Profile .HardwareIdentification.GetPackageSpecificToken(null); var hardwareId = token.Id; var bytes = new byte[hardwareId.Length]; using (var dataReader = Windows.Storage.Streams .DataReader.FromBuffer(hardwareId)) { dataReader.ReadBytes(bytes); } return BitConverter.ToString(bytes); } private static string GetUserId() { // TODO: Implement logic to retrieve the user id. return "UserID"; }
Zum Senden der Push-Benachrichtigungen mit Hilfe der eines "Tags" sähe der Code wie folgt aus:
public void SendMessage(string message, string tag) { var connectionString = ServiceBusConnectionStringBuilder .CreateUsingSharedAccessSecretWithFullAccess("<Service Bus Namespace>", "<Passwort des DefaultFullSharedAccessSignature Kontos>"); var hubClient = NotificationHubClient .CreateClientFromConnectionString(connectionString, "myhub"); hubClient.SendTemplateNotification(new Dictionary { {"msg", message} }, null, // Message-Body tag); }
Verwendete Bildquellen:
© Rainer Sturm / PIXELIO
Sorry hier nochmal der korrekte Link 😉 http://www.littlepostman.com/de/index.html