Smartwatch Text aus dem Internet herunterladen

Smartwatch Text aus dem Internet herunterladen

Wie erhalten wir Daten aus dem Internet an einem Gerät, dass selber kein Internetzugang besitzt? – Tja, dass ist wohl eine schwierige Frage. Zum Glück haben wir jedoch die Möglichkeit über einen kleinen Umweg Daten von unserer Smartwatch an unser Smartphone zu senden und anders herum genau so.

Leider gibt es eben hier kleinere oder auch größere Probleme, welche es zu überwinden gilt.
Erstens handelt es sich bei dieser Datenübertragung um  Fire-and-Forget Anfragen, wir können also im Grund nur hoffe, dass die Anfrage und damit die Daten auch ankommen. Eine mögliche Lösung wäre es einfach einen Time-Out zu programmieren. Bekommt unsere Smartwatch auch nach 30 Sekunden keine Daten zugesandt schicken wir einfach eine neue Anfrage.

Des Weiteren handelt es sich sowohl bei den Anfragen, als auch beim Herunterladen der Daten um Asynchrone Aufgaben. Dies bedeutet, dass wir in unserer Programmierung nicht auf eine Antworten warten können, sondern ohne die Daten vorerst weiter arbeiten müssen. Vorteil ist allerdings, dass unsere Smartwatch App nicht einfriert, bis die Daten ankommen und das kann schon einmal etwas länger dauern.

Zu guter letzt haben wir am Ende dann noch kleinere Probleme unsere erhaltenen Daten in unserer Hauptklasse verfügbar zu machen. Dazu aber später mehr.

Funktionsweise

Ich möchte vorab gerne klären, wie die anforderten Daten nun an die Smartwatch gelangen.

Die Hauptklasse der Smartwatch schickt eine Anfrage an unsere Smartphone App.
Diese Smartphone App regiert in einer Listener-Klasse auf die Anfrage und lädt die benötigten Daten herunter.
Weiter verbindet sich die Smartphone App mit den Google Servern, um eine Antwort an die Smartwatch senden zu können. Je nachdem welcher Vorgang (Verbindung zu Google oder herunterladen der Daten) schneller fertig ist wird die Antwort dann gesendet.
Die Smartwatch erhält die Daten dann in ihrer eigenen Listener-Klasse und ab dem Punkt müssen wir überlegen, wie wir die Daten in die Hauptklasse bekommen.

Smartphone

AndroidManifest.xml

Vorab benötigen wir einen API-Key von Google. Geht nach diesem Artikel vor: Google Cloud API-Key erhalten

Ändert die Datei AndroidManifest.xml der Smartphone App nun wie folgt:





 
 


 

 
 
 
 
 


 
 
 
 
 

 

 

ListenerService-Klasse

Wir erstellen nun eine Klasse namens “ListenerService” in der Smartphone App und füllen sie wie folgt:

package de.droid_lernen.droidface;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;

import java.io.UnsupportedEncodingException;

import cz.msebera.android.httpclient.Header;


public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks{
 
 private static String message;
 private static GoogleApiClient client;

 @Override
 public void onMessageReceived(MessageEvent messageEvent) {
 client = getGoogleApiClient(this);
 if( client != null && !(client.isConnected() || client.isConnecting()))
 client.connect();
 getData(messageEvent.getData().toString());
 }

 private GoogleApiClient getGoogleApiClient(Context context) {
 return new GoogleApiClient.Builder(context)
 .addApi(Wearable.API)
 .addConnectionCallbacks(this)
 .build();
 }

 private void reply() {
 new Thread( new Runnable() {
 @Override
 public void run() {

 if(client.isConnected() && message != null) {
 NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes( client ).await();
 for(Node node : nodes.getNodes()) {
 MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(
 client, node.getId(), "/antwort", message).await();
 Log.e("Sending Response", message);
 }
 client.disconnect();
 }

 }
 }).start();
 }

 private void getData(String apiUrl){
 AsyncHttpClient client = new AsyncHttpClient(true, 80, 443);
 client.get(apiUrl, null, new AsyncHttpResponseHandler() {

 @Override
 public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
 try {
 message = new String(responseBody, "UTF-8");
 Log.e("Response", message);
 reply();

 } catch (UnsupportedEncodingException e) {
 e.printStackTrace();
 }
 }

 @Override
 public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {

 }
 });
 }

 @Override
 public void onConnected(@Nullable Bundle bundle) {
 Log.e("Google API", "Connected");
 reply();
 }

 @Override
 public void onConnectionSuspended(int i) {

 }
}

Die Klasse erhält nun einen Url und sendet die von diesem Url erhaltenen Daten an alle verbundenen Geräte.
Bitte beachtet, dass die Funktion “reply” mehrfach aufgerufen wird.
Dies hat folgenden Grund: Für eine Antwort müssen wir erstens mit dem Google Play Service verbunden sein und wir benötigen die Antwort von der Url. Es werden nicht beide Dinge gleichzeitig fertig sein.
Ergo rufen wir “reply” einmal auf, wenn wir mit den Google Play Service verbunden sind und einmal wenn wir die Daten erhalten haben.
Innerhalb von “reply” prüfen wir dann, ob wir verbunden sind und die Daten erhalten haben. Die Aufgabe, die als letztes fertig ist wird das Senden der Antwort dann erst auslösen, da dann beide Bedingungen erfüllt sind.

Abhängigkeiten

Um es uns einfacher zu machen habe ich in diesem Artikel eine Library benutzt. Um diese in eurem Projekt nutzen können müsst ihr die Datei “gradle.build (Module: mobile)” öffnen und unter “dependencies” hinzufügen:

compile 'com.loopj.android:android-async-http:1.4.9'

Smartwatch

Fahren wir nun mit der Smartwatch App fort. Auch hier benötigen wir wieder einen Listener, der die Nachricht vom Smartphone abfängt. Zusätzlich benötigen wir noch eine Hauptklasse, die den ganzen Prozess starten kann.

AndroidManifest.xml

Für den Anfang benötigen wir eine Änderung in der AndroidManifest.xml-Datei der Smartwatch App.
Für fügen innerhalb des -Tags hinzu:


 
 
 

Prozess anstoßen

Um den gesamten Prozess anzustoßen muss unsere Smartwatch App unser Smartphone damit beauftragen.
Wir gehen nun in die Klasse, die den Prozess anstoßen soll und schreiben:

private static String nodeId = "";

private void sendRequest(final String pfad, final String url) {
 final GoogleApiClient client = new GoogleApiClient.Builder(this)
 .addApi(Wearable.API)
 .build();

 new Thread(new Runnable() {
 @Override
 public void run() {
 client.blockingConnect(10000, TimeUnit.MILLISECONDS);
 NodeApi.GetConnectedNodesResult result =
 Wearable.NodeApi.getConnectedNodes(client).await();
 List nodes = result.getNodes();
 if (nodes.size() > 0) {
 nodeId = nodes.get(0).getId();
 }
 }
 }).start();

 if (nodeId != null) {
 new Thread(new Runnable() {
 @Override
 public void run() {
 client.blockingConnect(10000, TimeUnit.MILLISECONDS);
 Wearable.MessageApi.sendMessage(client, nodeId, pfad, url.getBytes());
 client.disconnect();
 }
 }).start();
 }
}

Wir starten den Prozess dann mit dem Aufruf:

sendRequest("/nachricht", "URL von dem die Daten kommen");

ListenerServiceWear

Um es ein weiteres mal aufzugreifen.
Wir senden eine Nachricht an das Smartphone, dieses lädt die Daten und schickt sie uns zurück.
Hier greift dann der ListenerServiceWear.

Erstellt eine Klasse in eurer Smartwatch App mit dem Namen “ListenerServiceWear” und schreibt herein:

import android.util.Log;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;


public class ListenerServiceWear extends WearableListenerService {


 @Override
 public void onMessageReceived(final MessageEvent messageEvent) {

 Data.data = messageEvent.getPath();
 Log.e("Response on Wear", Data.data);
 }
 

}

An dieser Stelle muss ich nochmal mit einer Erklärung eingreifen.

Wir nehmen mal an wir haben eine App (Hauptklasse) auf der Smartwatch offen, starten eine Anfrage und bekommen auch recht flink eine Antwort (ListenerServiceWear).
Jetzt haben wir allerdings ein Problem. Wir benötigen die erhaltenen Daten in der  Hauptklasse, bekommen die Daten allerdings in ListenerServiceWear.
Wie bekommen wir die Daten nun von der einen in die andere Klasse?

Wir könnten die Daten in Einstellungen speichern, aber das ist auf einer Smartwatch nicht die beste Idee.
Wir erstellen uns einfach eine Hilfsklasse “Data” und erstellen in dieser eine static Variable “data”.
Wichtig ist das static, denn dann können wir in der Hauptklasse und im Listener ein Objekt vom Typ Data erstellen, die Variable im Listener ändern und sie ist im Objekt der Hauptklasse auch geändert.

Kleine Zusatzinfomation: Wenn wir ein Objekt erstellen und dort eine non-static Variable ändern, dann bleibt die selbe Variable der anderen Objekte vom gleichen Typ unverändert. Sie sind unabhängig.
Wenn wir ein Objekt erstellen und dort eine static Variable ändern, dann ändert sich die selbe Variable der anderen Objekte von gleichen Typ ebenfalls. Static Variablen werden also von allen Objekten des selben Typs geteilt.

Wir erstellen nun also eine Klasse mit dem Namen “Data” und schreiben:

public class Data{
 public static String data = "";
}

Wir können nun einfach in der Hauptklasse alle 10 Sekunden den Inhalt von Data.data auslesen und ändern und schon haben wir Text aus dem Internet geladen.

Marvin

Ich bin 23 Jahre jung und studiere zurzeit Wirtschaftsinformatik an der Georg-August-Universität in Göttingen. Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

Kommentar hinzufügen

*Pflichtfeld