Kolumne: XAML Expertise

XAML-Tipp: UWP – App-zu-App-Kommunikation mit App Services
Keine Kommentare

In der Kolumne „XAML Expertise“ präsentiert Gregor Biswanger Top-How-tos zum Thema XAML. Einsteiger und fortgeschrittene XAML-Experten sollen hier durch geballtes Wissen gesättigt werden. Heute gibt es folgende Expertise: „UWP: App-zu-App-Kommunikation mit App Services“

Eine dauerhafte Kommunikation zwischen unterschiedlichen Apps wäre nur über Web Services möglich. Das ist nicht immer die ideale Kombination. Es bedarf zusätzlich eines Webservers, und die Apps benötigen für diesen auch noch den Zugriff. Das bedeutet ebenfalls keine Offlineunterstützung für die App-zu-App-Kommunikation.

UWP: App-zu-App-Kommunikation mit App Services

Eine Alternative hierfür wäre ein Background Task. Dieser kann jederzeit von anderen Apps mit Daten befüllt werden oder gewünschte Daten abrufen. Soll die Host-App vom Background Task jedoch auch noch interaktiv auf die außenstehenden Anfragen reagieren, ist es gar nicht mehr so einfach. Der Grund hierfür ist, dass ein Background Task in einem eigenständigen Prozess läuft.

Eine attraktive Lösung bieten hier die „In-Process App Services“. Dabei handelt es sich ebenfalls um eine Art Background Task, der direkt von der Host-App ausgeführt wird. Beide laufen somit gemeinsam in einer Instanz.

Der erste Schritt beim Hinzufügen eines App Service ähnelt dem eines klassischen Background Tasks: per Doppelklick auf die Package.appxmanifest-Datei und unter DECLARATIONS einen App Service hinzufügen, wie in Abbildung 1 zu sehen. Der große Unterschied gegenüber der Registrierung eines Background Tasks ist, dass nur ein Name für den App Service benötigt wird. Der „Entry Point“ oder die „Start Page“ bleiben leer.

Abb. 1: App Service in der „Package.appxmanifest“-Datei festlegen

Windows 10 weiß somit beim Ausführen der Windows-App, dass es sich um einen In-Process App Service handelt. Es wird automatisch die OnBackgroundActivated-Methode von der App.xaml.cs-Datei ausgeführt. Diese liefert empfangene Anfragen über die BackgroundActivatedEventArgs. In Listing 1 wird gezeigt, wie eine eigene MyBackgroundTask-Klasse mit den EventArgs instanziiert wird.

sealed partial class App : Application
{
  private MyBackgroundTask _myBackgroundTask;

  protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
  {
    base.OnBackgroundActivated(args);
    _myBackgroundTask = new MyBackgroundTask(args);
  }
...

Die MyBackgroundTask-Klasse braucht gegenüber den klassischen Background Tasks keine Implementierung des IBackgroundTask-Interface. In-Process-BackgroundTasks unterstützen nicht die Aktivierung einer VoIP, DeviceUseTrigger, DeviceServicingTrigger und IoTStartup Task.

In Listing 2 sieht man eine Beispielimplementierung für die MyBackgroundTask-Klasse. Im Konstruktor wird eine dauerhafte AppServiceConnection-Instanz erzeugt, die auf Anfragen externer Apps lauscht. Wird eine Datenverbindung aufgebaut, erfolgt das RequestReceived-Event. Nach der Abfrage der Daten über die AppServiceRequestReceivedEventArgs wird das statische IncomingMessage-Event gefeuert und die Verbindung getrennt. Optional könnte man hierbei ebenfalls eine Nachricht an den Sender zurückschicken.

public class MyBackgroundTask
{
  public static event Action IncomingMessage;

  private AppServiceConnection appServiceConnection;
  private BackgroundTaskDeferral appServiceDeferral;

  public MyBackgroundTask(BackgroundActivatedEventArgs args)
  {
    IBackgroundTaskInstance taskInstance = args.TaskInstance;
    AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    appServiceDeferral = taskInstance.GetDeferral();
    appServiceConnection = appService.AppServiceConnection;
    appServiceConnection.RequestReceived += OnAppServiceRequestReceived;
  }

  private void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
  {
    AppServiceDeferral messageDeferral = args.GetDeferral();
    string text = args.Request.Message["message"].ToString();
    IncomingMessage?.Invoke(text);

    messageDeferral.Complete();
    appServiceDeferral.Complete();
  }
}

Das IncomingMessage-Event wird in diesem Beispiel von der MainPage.xaml.cs-Datei abonniert und die Oberfläche aktualisiert (Listing 3). Der Code zum Versenden von Daten an einen Data Service wird in Listing 4 gezeigt. Hierbei sind der App-Service-Name und der Package-Family-Name essenziell. Der App-Service-Name der Host-App wurde in der Package.appxmanifest-Datei hinterlegt (Abb. 1). Der korrekte Package-Family-Name befindet sich ebenfalls in der gleichen Manifestdatei, im Bereich „Packaging“ ganz unten. Wenn beide Apps miteinander über App Services kommunizieren, hat man ein Ergebnis wie in Abbildung 2 gezeigt.

public sealed partial class MainPage : Page
{
  public MainPage()
  {
    this.InitializeComponent();
    MyBackgroundTask.IncomingMessage += OnIncomingMessage;
  }

  private async void OnIncomingMessage(string message)
  {
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
      tbMessages.Text = tbMessages.Text + "\n" + message;
    });
  }
...
private async void Button_Click(object sender, RoutedEventArgs e)
{
  using (var connection = new AppServiceConnection())
  {
    connection.AppServiceName = "InProcessAppService";
    connection.PackageFamilyName = "d9855a58-f231-4a8e-a1b0-79fd4e9bcf67_0fbekf7tvynfp";
    var status = await connection.OpenAsync();

    var inputs = new ValueSet();
    inputs.Add("message", "Client App: " + tbMessage.Text);
    var response = await connection.SendMessageAsync(inputs);

    tbMessages.Text = tbMessages.Text + "\n" + tbMessage.Text;
    tbMessage.Text = "";
  }
}

Abb. 2: Zwei Apps kommunizieren über App Services

Unsere Redaktion empfiehlt:

Relevante Beiträge

X
- Gib Deinen Standort ein -
- or -