Apps für Android programmieren leicht gemacht!

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:

<?xml version="1.0" encoding="utf-8"?>

<manifest ...>

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />


    <application ...>

        <service android:name=".ListenerService" >
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
            </intent-filter>
        </service>


        <!-- API Key zum Debuggen -->
        <meta-data android:name="com.google.android.gms.games.APP_ID"
        android:value="[EUER API KEY]" />
    
        <!-- API Key zum Release -->
        <!--<meta-data android:name="com.google.android.gms.games.APP_ID"
        android:value="[EUER API KEY]" />-->

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>

 

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'

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