Von App zu App

Windows Phone 8: Kommunikation via Bluetooth
Kommentare

Mit der Einführung von Windows Phone 8 wurden neue Möglichkeiten der Kommunikation via Bluetooth direkt aus der App heraus bereitgestellt. Der folgende Artikel gibt einen Überblick über die Optionen zum Aufbau und zur Verwendung einer Verbindung.

Auf dem Windows Phone 7 gab es für Apps nur die Möglichkeit, über IP-Verbindungen zu kommunizieren, was eine lokale Verbindung durch verschiedene Netzwerkinfrastrukturen (z. B. Firewalls) oft erschwerte. Mit der neuen Version Windows Phone 8 kommen zwei weitere Möglichkeiten zur lokalen Kommunikation hinzu: NFC (Near Field Communication) ist für besonders kurze Distanzen von ein bis zwei Zentimetern und kleinere Datenmengen besonders gut geeignet (siehe Windows Developer 11.2013). Wenn jedoch größere Datenmengen über eine Distanz von bis zu zehn Metern übertragen werden sollen, bietet sich Bluetooth an. Bluetooth kann in zwei verschiedenen Szenarien verwendet werden: Eine App kann eine Verbindung zu einem externen Gerät aufbauen, hierbei spricht man von der App2Device-Kommunikation. Ein Beispiel dafür ist die Verwendung einer GPS-Maus mit dem Telefon direkt aus der App heraus. In diesem Fall ist es erforderlich, das Gerät vor der Verwendung in der App mit dem Telefon zu koppeln. Die zweite Möglichkeit ist, dass eine App mit der gleichen oder einer anderen App auf einem zweiten Smartphone oder einem Windows-8-PC kommuniziert. In diesem Fall ist keine Kopplung außerhalb der App erforderlich. In beiden Szenarien wird ein StreamSocket bereitgestellt, mit dessen Hilfe Nachrichten zwischen den Kommunikationspartnern ausgetauscht werden können.

App2Device-Szenario

Bevor eine App jedoch Zugriff auf das Bluetooth-API des Telefons bekommt, muss diese Funktion im WMAppMainifest.xml beim Benutzer angefordert und bei der Installation von ihm bestätigt werden. Öffnen Sie dazu die Datei WMAppManifest.xml und aktivieren die Funktionen ID_CAP_PROXIMITY und ID_CAP_NETWORKING. Zum Verbindungsaufbau mit einem Gerät muss dieses zunächst mit dem Windows Phone gekoppelt werden (engl. pairing). Anschließend können alle gekoppelten Geräte mithilfe der Klasse PeerFinder ermittelt werden. Setzt man den Filter für die verbundenen Geräte auf eine leere Zeichenkette, werden alle Geräte angezeigt.

try{
  PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
  var peers = await PeerFinder.FindAllPeersAsync();
} catch (Exception ex) {
  if ((uint)ex.HResult == 0x8007048F)
  MessageBox.Show("Bluetooth is switched off");
}

Sollte Bluetooth deaktiviert sein, wird eine Exception geworfen, die ein entsprechendes HResult enthält. An dieser Stelle kann entweder eine Fehlermeldung ausgegeben werden, die den Anwender dazu auffordert, Bluetooth zu aktivieren, oder der Anwender kann direkt zu dem entsprechenden Einstellungsdialog weitergeleitet werden. Auf dem Windows Phone 8 gibt es zwei Möglichkeiten, die Verbindungseinstellungen aus einer App heraus aufzurufen. Die erste Möglichkeit ist die Verwendung des ConnectionSettingsTask:

ConnectionSettingsTask connectionSettingsTask = new ConnectionSettingsTask();
connectionSettingsTask.ConnectionSettingsType = ConnectionSettingsType.Bluetooth;
connectionSettingsTask.Show();

Alternativ gibt es für (fast) alle Einstellungen und Betriebssystemdialoge ein eigenes URI-Schema, mit dessen Hilfe der entsprechende Dialog aufgerufen werden kann:

Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-bluetooth"));

Nachdem sichergestellt wurde, dass Bluetooth zur Verfügung steht, kann die Abfrage der bekannten Peers fortgesetzt werden. Das Ergebnis der Funktion FindAllPeersAsync ist eine Liste von Peer-Informationen. Diese Klasse enthält den Anzeigenamen, den Hostnamen, den Servicenamen und weitere Informationen. Die Verbindung zu einem Peer wird mithilfe eines StreamSockets aufgebaut. Zunächst wählt man aus der Liste der verfügbaren Peers einen aus, anschließend verbindet man ihn mit der Funktion ConnectAsync des StreamSockets:

// Für das Beispiel wird der erste Peer gewählt
PeerInformation partner = peers[0];
// Versuch, die Verbindung aufzubauen
StreamSocket socket = new StreamSocket();
await socket.ConnectAsync(partner.HostName, partner.ServiceName);

Zum leichteren Zugriff auf einen StreamSocket empfiehlt es sich an dieser Stelle, die Hilfsklassen StreamRader und StreamWriter zu verwenden.

Externe GPS-Maus

Im folgenden Beispiel soll eine App entstehen, die eine Verbindung zu einer externen GPS-Maus aufbaut, um die aktuelle Position mithilfe des NMEA-Protokolls an das Telefon zu übermitteln. Da NMEA-Nachrichten jeweils aus einer Zeile bestehen, ist das Protokoll sehr einfach zu implementieren.  Die einzelnen Zeilen im NMEA-Protokoll verfügen über keine Längeninformationen, sondern nur über ein CR-Zeichen, das das Ende einer Zeile markiert. Aus diesem Grund muss der Datenstrom Zeichen für Zeichen gelesen werden. Zur Vereinfachung der Kommunikation sollten alle Funktionen rund um die Verbindung und Übertragung einzelnen Zeilen in die Hilfsklasse ConnectionManager ausgelagert werden.

public class ConnectionManager {
  private byte[] _buffer;
  private int _currentPos;

  private StreamSocket _socket;
  private DataWriter _dataWriter;
  private DataReader _dataReader;
  private BackgroundWorker _dataReadWorker;

  public delegate void MessageReceivedHandler(string message);
  public event MessageReceivedHandler MessageReceived;

  public void Initialize() {
    _socket = new StreamSocket();
    _dataReadWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
    _dataReadWorker.DoWork += ReceiveMessages;

    _buffer = new byte[512];
    _currentPos = 0;
  }

Neben einem Puffer für die aktuelle Zeile und Position, den StreamSocket, DataReader und DataWriter, wird auch ein BackgroundWorker benötigt. Dieser Worker wird nach dem Verbindungsaufbau warten, bis ein oder mehr Zeichen zur Verfügung stehen, um diese dann einzeln auszulesen und ggf. das Ereignis MessageReceived mit der aktuellen NMEA-Zeile zu werfen, wenn diese komplett ist:

public async Task Connect(PeerInformation peer) {
  if (_socket != null) {
    await _socket.ConnectAsync(peer.HostName, (String.IsNullOrWhiteSpace(peer.ServiceName)) ? "1" : peer.ServiceName);
    _dataReader = new DataReader(_socket.InputStream);
    _dataReadWorker.RunWorkerAsync();
    _dataWriter = new DataWriter(_socket.OutputStream);
  }
}

Die Funktion Connect versucht, eine Verbindung aufzubauen, wenn der StreamSocket noch nicht verbunden ist. Sollte die PeerInformation keinen Servicenamen enthalten, wird 1 als Standardwert angenommen, denn bei den meisten externen GPS-Mäusen ist dies der Standard für den NMEA-Nachrichtenstrom. Anschließend wird ein DataReader sowie ein DataWriter erstellt und der WorkerThread für das Empfangen von Nachrichten wird gestartet.

private async void ReceiveMessages(object sender, DoWorkEventArgs e) {
  try {
    while (true) {
      uint sizeFieldCount = await _dataReader.LoadAsync(1);
      if (sizeFieldCount != 1) {
        // Socket wurde geschlossen 
        return;
      }
      
      byte b = _dataReader.ReadByte();
      _buffer[_currentPos++] = b;

      if (b == 0x0A) {
        if (MessageReceived != null) {
          MessageReceived(Encoding.UTF8.GetString(_buffer, 0, _currentPos));
        }
        _currentPos = 0;
      }
    }
  } catch (Exception ex) {
    Debug.WriteLine(ex.Message);
  }
}

Die Funktion ReceiveMessages besteht aus einer Endlosschleife, die so lange auf ankommende Nachrichten wartet, bis die Verbindung geschlossen wird. Dabei ist nicht relevant, welche Seite die Verbindung zuerst geschlossen hat. Da das NMEA-Protokoll weder einen Header noch eine Längenangabe für die einzelnen Nachrichten hat, muss hier Zeichen für Zeichen ausgelesen und auf das Zeilenende hin geprüft werden. Mit dem Erreichen des Zeilenendes wird das Ereignis MessageReceived ausgelöst und die empfangende Zeile übergeben. Die Funktion Terminate beendet eine bestehende Verbindung und somit auch die Empfangsschleife:

public void Terminate() {
  if (_socket != null) {
    _socket.Dispose();
  }
  if (_dataReadWorker != null) {
    _dataReadWorker.CancelAsync();
  }
}

Auf der Oberfläche sind zwei Buttons erkennbar. Der eine zeigt eine Liste der verfügbaren Geräte an, der andere baut nach Auswahl eines Geräts eine Verbindung mit diesem auf. Für die Auswahl wird eine ListBox mit einem entsprechenden ItemTemplate verwendet. Da das Bluetooth-API im Emulator nicht benutzt werden kann, wird nach dem Laden der Hauptseite (MainPage.xaml) zunächst überprüft, ob die App im Emulator läuft und anschließend ein Hinweis ausgegeben.

private void MainPage_Loaded(object sender, RoutedEventArgs e) {
  // Bluetooth is not available in the emulator.
  if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Emulator) {
    MessageBox.Show(AppResources.Msg_EmulatorMode, "Warning", MessageBoxButton.OK);
  }

  _pairedDevices = new ObservableCollection<PairedDeviceInfo>();
  PairedDevicesList.ItemsSource = _pairedDevices;
}

Der Ereignis-Handler RefreshPairedDevicesList ruft alle zur Verfügung stehenden Geräte ab und aktualisiert die ObservableCollection mit den PairedDeviceInfos. Sollte Bluetooth deaktiviert sein, wird der entsprechende Einstellungsdialog angezeigt. Eine Nachricht informiert darüber, falls auf diesem Gerät kein Bluetooth verfügbar ist.

private async void RefreshPairedDevicesList() {
  try {
    // verbundene Geräte suchen 
    PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
    var peers = await PeerFinder.FindAllPeersAsync();

    // mit Leeren der Liste wird auch die ListBox geleert 
    _pairedDevices.Clear();

    if (peers.Count == 0) {
      MessageBox.Show(AppResources.Msg_NoPairedDevices);
    } else {
      // gefundene verbundene Geräte hinzufügen. 
      foreach (var peer in peers) {
        _pairedDevices.Add(new PairedDeviceInfo(peer));
      }
    }
  } catch (Exception ex) {
    if ((uint) ex.HResult == 0x8007048F) {
      var result = MessageBox.Show(AppResources.Msg_BluetoothOff, "Bluetooth Off", MessageBoxButton.OKCancel);
      if (result == MessageBoxResult.OK) {
        var connectionSettingsTask = new ConnectionSettingsTask {ConnectionSettingsType = ConnectionSettingsType.Bluetooth};
        connectionSettingsTask.Show();
      }
    } else if ((uint) ex.HResult == 0x80070005) {
      MessageBox.Show(AppResources.Msg_MissingCaps);
    } else {
      MessageBox.Show(ex.Message);
    }
  }
}

Der Ereignis-Handler PairedDevicesList_SelectionChanged aktiviert ein zusätzliches Eingabefeld für den Servicenamen, falls diese Angabe für das entsprechende Gerät erforderlich ist. Die meisten GPS-Mäuse erfordern jedoch keinen Eintrag bzw. die Verwendung von 1 als Servicenamen.

private void PairedDevicesList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  // prüfen, ob ein Gerät gewählt ist 
  if (PairedDevicesList.SelectedItem == null) {
    // falls nicht UI Elemente deaktivieren 
    ConnectToSelected.IsEnabled = false;
    ServiceNameInput.Visibility = Visibility.Collapsed;
  } else {
    // falls ja, UI-Elemente aktivieren
    ConnectToSelected.IsEnabled = true;

    // TextBox für den Servicenamen anzeigen
    var pdi = PairedDevicesList.SelectedItem as PairedDeviceInfo;
    ServiceNameInput.Visibility = (String.IsNullOrWhiteSpace(pdi.ServiceName)) ? Visibility.Visible : Visibility.Collapsed;
  }
}

Nachdem der Anwender auf den Connect-Button geklickt hat, wird der ConnectionManager verbunden und ein „Enter“ an das Gerät gesendet. Bei den meisten externen GPS-Mäusen entspricht das der Aufforderung, mit dem Senden von Positionsdaten zu beginnen. Hinweis: Wenn die von Ihnen verwendete GPS-Maus nicht damit beginnt, NMEA-Nachrichten zu senden, kann es ggf. erforderlich sein, eine andere initiale Befehlssequenz zu senden. Welche das ist, schlagen Sie bitte in der Anleitung zu Ihrer GPS-Maus nach:

private async void ConnectToSelected_Tap_1(object sender, GestureEventArgs e) {
  // Connect to the device 
  var pdi = PairedDevicesList.SelectedItem as PairedDeviceInfo;
  await _connectionManager.Connect(pdi.PeerInfo);
  var res = _connectionManager.SendCommand(Environment.NewLine);
}

Da in diesem Artikel die Kommunikation via Bluetooth im Vordergrund steht, ist der NMEA-Parser für dieses Beispiel stark vereinfacht. Weitere Informationen rund um dieses Nachrichtenformat erhalten Sie hier. Im Ereignis-Handler für das MessageReceived-Ereignis wird die aktuelle Nachricht zerlegt und die einzelnen Bestandteile werden im Rohformat ausgegeben.

public MainPage() {
  InitializeComponent();
  _connectionManager = new ConnectionManager();
  _connectionManager.Initialize();
  _connectionManager.MessageReceived += message =>
  {

    object[] attributes = message.Split(new [] { (char)44, (char)42 });
    if ((attributes.Length == 16) && (attributes[0].ToString() == "$GPGGA")) {
      Deployment.Current.Dispatcher.BeginInvoke(() => {
        tbResult.Text = string.Format("{1} | {2},{3} | {4},{5} - {6} \r\nSatelites:{7} - HDop:{8} Alt:{9} High:{10}", attributes);
      });
    }
  };
}

 

fischer_bluetooth_1

Abb. 1: Beispielanwendung für eine externe GPS-Maus in Aktion

App2App-Szenario

Genauso wie bei der Kommunikation zwischen einer App und einem externen Gerät, muss auch hier die Funktion ID_CAP_PROXIMITY aktiviert sein (siehe WMAppMainfest.xml). In diesem Szenario wird der PeerFinder zur Bereitstellung eines Ereignisses verwendet, wenn der Kommunikationswunsch einer App eintrifft. Anschließend wird die Verbindung mithilfe einer StreamSocket-Klasse aufgebaut. Dabei ist es nicht erforderlich, die Geräte vorher zu koppeln.

 

fischer_bluetooth_2

Abb. 2: Aufbau einer Verbindung mithilfe des „PeerFinders“

Bevor der PeerFinder eine App finden kann, muss diese zunächst Ihre Anwesenheit signalisieren. Das kostet Energie und geht zu Lasten der Batterielaufzeit, da die Geräte A und C nach dem Aufrufen der Startfunktion des PeerFinders in regelmäßigen Abständen Rundnachrichten an alle Empfänger verschicken. Das Windows Phone B sammelt diese Nachrichten ein und stellt nach dem Aufruf der asynchronen Funktion FindAllPeers() eine Liste aller empfangenen kommunikationsbereiten Geräte zusammen. Nachdem auf dem Gerät B das Windows Phone A ausgewählt wurde, wird die Funktion ConnectAsync vom Windows Phone B ausgehend aufgerufen. Auf dem Windows Phone A wiederum wird ein Ereignis ausgelöst, welches über die eingehende Verbindung informiert. Nun wird auch auf dem Windows Phone A die Funktion ConnectAsync mit dem Windows Phone B aufgerufen. Die jeweilige Instanz des PeerFinders stellt danach auf beiden Telefonen ein StreamingSocket zur Verfügung. Wird die Verbindung vom Gerät A nicht innerhalb einer vorgegebenen Zeit bestätigt, läuft Windows Phone B in einen TimeOut. Es gibt keine Möglichkeit, eine Verbindung durch Aufrufen einer Funktion abzulehnen. Zur besseren Übersichtlichkeit sollen auch in diesem Szenario alle Kommunikationsfunktionen in die Klasse ConnectionManager gekapselt werden.

public class ConnectionManager {
  private StreamSocket _socket;
  private string _peerName = string.Empty;

  public delegate void MessageReceivedHandler(string message);
  public event MessageReceivedHandler MessageReceived;
  public delegate void PeerConnectedHandler();
  public event PeerConnectedHandler PeerConnected;

  private DataWriter _dataWriter;
  private DataReader _dataReader;
  private readonly BackgroundWorker _dataReadWorker;

Neben einem Ereignis MessageReceived für eine empfangene Nachricht bekommt dieser ConnectionManager ein weiteres Ereignis PeerConnected, das immer dann gefeuert wird, wenn eine Verbindung erfolgreich aufgebaut werden konnte. Der Handler für den Verbindungswunsch eines anderen Peers wird im Konstruktor initialisiert, wo auch der WorkerThread ausprogrammiert wurde. Das Protokoll für die Übertragung der Chatnachrichten kann frei implementiert werden, da die Implementierung auf beiden Seiten separat erfolgt. Für dieses Beispiel soll als Erstes die Länge der Nachricht als 4-Byte (Int32) übertragen werden, als Zweites die Nachricht selbst.

public ConnectionManager() {
  PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
  _dataReadWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
  _dataReadWorker.DoWork += async (sender, args) =>
  {
    try {
      while (true) {
        // Jede Nachricht hat eine Länge (Int32)
        await _dataReader.LoadAsync(4);
        var messageLen = (uint) _dataReader.ReadInt32();
        await _dataReader.LoadAsync(messageLen);
        if (MessageReceived != null) {
          MessageReceived(_dataReader.ReadString(messageLen));
        }
      }
    } catch (Exception ex) {
      Debug.WriteLine(ex.Message);
    }
  };
}

Die Empfangsfunktion ist als Endlosschleife programmiert, die mit dem Schließen des StreamSocket beendet wird. Um ungewollte Verbindungen zu verhindern, sendet der Event Handler eine Anfrage zum Verbindungsaufbau an den Benutzer. Wenn der Benutzer die Verbindung akzeptiert, wird die Funktion ConnectToPeer aufgerufen.

private void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args) {
  try {
    Deployment.Current.Dispatcher.BeginInvoke(async () =>
    {
      // Den Benutzer fragen, ob die Verbindung aufgebaut werden soll.
      var result = MessageBox.Show(String.Format(AppResources.Msg_ChatPrompt, args.PeerInformation.DisplayName)
        , AppResources.Msg_ChatPromptTitle, MessageBoxButton.OKCancel);
      if (result == MessageBoxResult.OK) {
        if (await ConnectToPeer(args.PeerInformation)) {
          if (PeerConnected != null) {
            PeerConnected();
          }
        };
      } else {
        // das API hat keine Funktion, die aufgerufen werden kann, um eine Verbindung abzulehnen.
      }
    });
  } catch (Exception ex) {
    MessageBox.Show(ex.Message);
    Terminate();
  }
}

Die Funktion ConnectToPeer stellt die Verbindung zum übergebenen Peer her. Nachdem der StreamSocket erfolgreich verbunden werden konnte, werden die DataReader und DataWriter erstellt. Anschließend wird der Worker gestartet, der auf ankommende Nachrichten wartet.

public async Task<bool> ConnectToPeer(PeerInformation peer) {
  try {
    _socket = await PeerFinder.ConnectAsync(peer);

    if (_socket != null) {
      // Den PeerFinder stoppen, um Energie zu sparen 
      PeerFinder.Stop();
      _peerName = peer.DisplayName;
      _dataReader = new DataReader(_socket.InputStream);
      _dataWriter = new DataWriter(_socket.OutputStream);
      _dataReadWorker.RunWorkerAsync();
      return true;
    }

  } catch (Exception ex) {
    MessageBox.Show(ex.Message);
    Terminate();
  }
  return false;

}

Nachrichten werden mithilfe der Funktion SendMessage verschickt. Für dieses Beispiel wurde festgelegt, dass die Nachricht einen Kopf enthalten soll, der die Länge der Nachricht beinhaltet. Nach dem Ermitteln der Länge wird diese als Int32-Wert auf dem StreamSocket übertragen, gefolgt von der Nachricht selbst:

public async void SendMessage(string message) {
  _dataWriter.WriteInt32(message.Length);
  await _dataWriter.StoreAsync();

  _dataWriter.WriteString(message);
  await _dataWriter.StoreAsync();
}

Nach dem Start der App wird als Erstes der PeerFinder aktiviert, anschließend wird der ConnectionManager initialisiert. Dieser verfügt über zwei Ereignisse. MessageReceived wird immer dann geworfen, wenn eine Nachricht angekommen ist, PeerConnected nach dem erfolgreichen Verbinden mit der Gegenseite.

// Constructor
public MainPage() {
  InitializeComponent();

  PeerFinder.Start();

  _connectionManager = new ConnectionManager();
  _connectionManager.MessageReceived += message => 
  Deployment.Current.Dispatcher.BeginInvoke(() => {
    tbResult.Text += string.Format("-> {0}\r\n", message);
  });
  _connectionManager.PeerConnected += () => 
  Deployment.Current.Dispatcher.BeginInvoke(() => 
  ToggleUI(true));
}

Um dem Benutzer die Möglichkeit zu geben, einen Kommunikationspartner auswählen zu können, ruft der Ereignis-Handler btnRefresh_Click die Liste aller verfügbaren Peers ab und stellt diese in einer ListBox dar.

private async void btnRefresh_Click(object sender, System.Windows.RoutedEventArgs e) {
  try {

    var peers = await PeerFinder.FindAllPeersAsync();
    PeerList.ItemsSource = (peers ?? new PeerInformation[] {}).Select(p => new PeerAppInfo(p));
  } catch (Exception ex) {
    if ((uint)ex.HResult == 0x8007048F) {
      var result = MessageBox.Show(AppResources.Err_BluetoothOff, AppResources.Err_BluetoothOffCaption, MessageBoxButton.OKCancel);
      if (result == MessageBoxResult.OK) {
        var connectionSettingsTask = new ConnectionSettingsTask { ConnectionSettingsType = ConnectionSettingsType.Bluetooth };
        connectionSettingsTask.Show();
      }
    } else if ((uint)ex.HResult == 0x80070005) {
      // You should remove this check before releasing. 
      MessageBox.Show(AppResources.Err_MissingCaps);
    } else if ((uint)ex.HResult == 0x8000000E) {
      MessageBox.Show(AppResources.Err_NotAdvertising);
    } else {
      MessageBox.Show(ex.Message);
    }
  }
}

Nach der Auswahl eines Peers wird versucht, unter Verwendung des ConnectionManagers eine Verbindung zum ausgewählten Kommunikationspartner aufzubauen. Ist die Verbindung aufgebaut, werden die UI-Elemente für die Auswahl des Peers ausgeblendet und die Elemente für das Schreiben von Nachrichten eingeblendet.

private async void PeerList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
  // Verbindung zum ausgewählten Peer aufbauen.
  var pdi = PeerList.SelectedItem as PeerAppInfo;
  var peer = pdi.PeerInfo;

  var res = await _connectionManager.ConnectToPeer(peer);
  tbResult.Text = (res) 
    ? "Sucessfull connected\r\n" 
    : "Could not connect \r\n";
    ToggleUI(res);

}

 

Abb. 3: Verbundene Bluetooth-Chat-App

Abb. 3: Verbundene Bluetooth-Chat-App

Bluetooth und der Lifecycle

In diesem Artikel wurde gezeigt, wie eine Verbindung von einer Windows-Phone-App zu einem Bluetooth-Gerät oder zu einer anderen App hergestellt werden kann. Diese Verbindung wird unterbrochen, wenn die App in den tombstoned-Zustand geht. Um die Ausführung nach dem Aktivieren der App fortsetzen zu können, muss man die Informationen über die aktuelle Verbindung sichern. Speichern Sie dazu folgende Informationen über den aktuellen Kommunikationspartner im ApplicationState:

PhoneApplicationService.Current.State["RemoteHostName"] = 
  socket.Information.RemoteHostName.RawName;
PhoneApplicationService.Current.State["RemoteServiceName"] = 
  socket.Information.RemoteServiceName;

Zur Wiederherstellung der Verbindung laden Sie zunächst die Information aus dem ApplicationState und rufen anschießend ConnectAsync des Sockets auf:

string storedRemoteHostRawName = 
  PhoneApplicationService.Current.State["RemoteHostName"] as string;
string storedRemoteServiceName = 
  PhoneApplicationService.Current.State["RemoteServiceName"] as string;
HostName newRemoteHostName = new HostName(storedRemoteHostRawName);
await socket.ConnectAsync(newRemoteHostName, storedRemoteServiceName);

Eine Wiederherstellung ist nur dann möglich, wenn der Kommunikationspartner verfügbar ist und sich nicht im Zustand dormant oder tombstoned befindet.

Die vollständigen Quellcodes der Beispiele in diesem Artikel finden Sie hier.

 

Aufmacherbild: PDA and wave (done in 3d) von Shutterstock / Urheberrecht: Vladru

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -