Java WebSocket Programmierung mit Android und Spring Boot

PubNub Developer Relations - Jan 17 - - Dev Community

Das WebSocket-Protokoll bietet eine ständig aktive Verbindung zwischen einem Client und einem Server für bidirektionale Kommunikation. Dies ist ideal für Anwendungen, die eine Echtzeit-Verbindung erfordern, wie z. B. Multiplayer-Spiele, Internet-of-Things-Anwendungen und Chat-Apps. Der Anwendungsfall für dieses Tutorial ist eine Chat-Anwendung, ein klassisches Beispiel für Echtzeitkommunikation. In diesem Tutorial werden wir einen einfachen Android-Client einrichten, der sich mit Hilfe von Spring Boot mit einem WebSocket-Server verbinden wird.

Java WebSocket-Client

Für den Android-Client werden wir eine einfache Demo-Applikation erstellen, die vier Bildschaltflächen mit niedlichen Tieren enthält. Bevor wir in die Java-Implementierung eintauchen, ist es erwähnenswert, dass Kotlin eine beliebte Wahl für die Android-Entwicklung geworden ist. Um zu beginnen, initialisieren Sie ein neues Projekt in Android Studio mit einer Basic Activity namens JavaWebSocketClient. Wir werden eine leichtgewichtige WebSocket-Client-Bibliothek für die App verwenden, die in diesem GitHub Repo zu finden ist. Um diese Bibliothek zu verwenden, müssen wir sie zur build.gradle-Datei im App-Verzeichnis hinzufügen. Fügen Sie Folgendes zu den Abhängigkeiten hinzu und synchronisieren Sie das Projekt:

dependencies { 
    // Add this
    implementation 'tech.gusavila92:java-android-websocket-client:<latest-version>'    
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    ...
}
Enter fullscreen mode Exit fullscreen mode

Stellen Sie sicher, dass die Internet-Zugriffsberechtigung in der Manifestdatei enthalten ist:

<uses-permission android:name="android.permission.INTERNET" />
Enter fullscreen mode Exit fullscreen mode

Verbinden Sie den Client mit dem Server

Gehen Sie zu MainActivity.java, importieren Sie die folgenden Pakete und richten Sie onCreate() ein:

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import java.net.URI;
import java.net.URISyntaxException;
import tech.gusavila92.websocketclient.WebSocketClient;

public class MainActivity extends AppCompatActivity {
  private WebSocketClient webSocketClient;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.animal_sounds);
    createWebSocketClient();
  }
}
Enter fullscreen mode Exit fullscreen mode

Als nächstes erstellen Sie eine neue Methode createWebSocketClient():

private void createWebSocketClient() {
    URI uri;
    try {
      // Connect to local host
      uri = new URI("ws://10.0.2.2:8080/websocket");
    }
    catch (URISyntaxException e) {
      e.printStackTrace();
      return;
    }

    webSocketClient = new WebSocketClient(uri) {
      @Override
      public void onOpen() {
        Log.i("WebSocket", "Session is starting");
        webSocketClient.send("Hello World!");
      }

      @Override
      public void onTextReceived(String s) {
        Log.i("WebSocket", "Message received");
        final String message = s;
        runOnUiThread(new Runnable() {
          @Override
          public void run() {
            try{
              TextView textView = findViewById(R.id.animalSound);
              textView.setText(message);
            } catch (Exception e){
                e.printStackTrace();
            }
          }
        });
      }

      @Override
      public void onBinaryReceived(byte[] data) {
      }

      @Override
      public void onPingReceived(byte[] data) {
      }

      @Override
      public void onPongReceived(byte[] data) {
      }

      @Override
      public void onException(Exception e) {
        Log.e("WebSocket", e.getMessage());
      }

      @Override
      public void onCloseReceived() {
        Log.i("WebSocket", "Closed ");
      }
    };

    webSocketClient.setConnectTimeout(10000);
    webSocketClient.setReadTimeout(60000);
    webSocketClient.enableAutomaticReconnection(5000);
    webSocketClient.connect();
  }
Enter fullscreen mode Exit fullscreen mode

Das sieht vielleicht nach viel aus, aber in Wirklichkeit erledigen wir in dieser Methode vier wichtige Dinge:

  1. Starten einer neuen WebSocket-Verbindung zum lokalen Host "ws://10.0.2.2:8080/websocket".

  2. Senden einer Nachricht an den Server, sobald eine Verbindung geöffnet ist.

  3. Anzeige der vom Server gesendeten Nachrichten in der App.

  4. Einstellen von Zeitüberschreitungen und automatischer Wiederherstellung der Verbindung.

Nachdem wir nun den Client mit dem Server verbunden haben, wollen wir die Methode zum Senden von Nachrichten an den Server einrichten.

Nachrichten an den Server senden

Fügen Sie in MainActivity.java folgendes zu sendMessage() hinzu:

public void sendMessage(View view) {
   Log.i("WebSocket", "Button was clicked");

   // Send button id string to WebSocket Server
   switch(view.getId()){
     case(R.id.dogButton):
       webSocketClient.send("1");
       break;

     case(R.id.catButton):
       webSocketClient.send("2");
       break;

     case(R.id.pigButton):
       webSocketClient.send("3");
       break;

     case(R.id.foxButton):
       webSocketClient.send("4");
       break;
   }
 }
Enter fullscreen mode Exit fullscreen mode

Wenn ein Button gedrückt wird, wird die Button-ID an den Server gesendet. Diese Methode wird aus der Datei animal_sounds.xml aufgerufen, die Sie aus meinem Java WebSocket Programming Repo beziehen können. Vergewissern Sie sich, dass Sie die Datei strings.xml unter dem Verzeichnis values ändern, damit Sie keine Fehler in der XML-Datei erhalten.

Das letzte, was auf der Client-Seite zu tun ist, ist das Hinzufügen der Bilder für die Tiere in das drawable-Verzeichnis. Die Bilder werden von Freepik von www.flaticon.com erstellt .

Starten Sie die Android-App in Ihrem Emulator:

Im Moment passiert nichts, weil der Server nicht eingerichtet ist. Lassen Sie uns das jetzt tun!

Spring Boot WebSocket-Server

Für unseren Server werden wir Spring Boot verwenden, das die Erstellung produktionsreifer Spring-Anwendungen mit minimalen Konfigurationen erleichtert. Um unser Projekt schnell zu booten, werden wir Spring Initializr verwenden. Wir werden ein Gradle-Projekt erzeugen, aber Sie können auch ein Maven-Projekt erzeugen, wenn Sie dies bevorzugen. Konfigurieren Sie Initializr wie im Screenshot unten und stellen Sie sicher, dass Sie WebSocket als eine Abhängigkeit hinzufügen:

Erzeugen Sie das Projekt, um eine Zip-Datei herunterzuladen. Sobald Sie die Datei entpackt haben, gehen Sie in das src-Verzeichnis und klicken Sie weiter auf die Unterverzeichnisse, bis Sie zur Datei JavaWebSocketServerApplication.java gelangen.

package com.example.javawebsocketserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JavaWebSocketServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(JavawebsocketserverApplication.class, args);
  }
}
Enter fullscreen mode Exit fullscreen mode

Fügen Sie dem Verzeichnis zwei Dateien hinzu: WebSocketHandler.java und WebSocketConfiguration.java.

Behandeln Sie die WebSocket-Nachrichten

Wir müssen die eingehenden Nachrichten, die auf dem Server ankommen, verarbeiten. Dazu erben Sie in WebSocketHandler.java die Klasse zur Implementierung der Methode handleTextMessage(). Fügen Sie den folgenden Code in die Datei ein:

package com.server.javawebsocketserver;

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import java.io.IOException;

public class WebSocketHandler extends AbstractWebSocketHandler {
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        String msg = String.valueOf(message.getPayload());
        // Send back a unique message depending on the id received from the client
        switch(msg){
            case("1"):
                Log.i("WebSocket", "Dog button was pressed");
                session.sendMessage(new TextMessage("Woooof"));
                break;

            case("2"):
                Log.i("WebSocket", "Cat button was pressed");
                session.sendMessage(new TextMessage("Meooow"));
                break;

            case("3"):
                Log.i("WebSocket", "Pig button was pressed");
                session.sendMessage(new TextMessage("Bork Bork"));
                break;

            case("4"):
                Log.i("WebSocket", "Fox button was pressed");
                session.sendMessage(new TextMessage("Fraka-kaka-kaka"));
                break;

            default:
                Log.i("WebSocket", "Connected to Client");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Innerhalb der Methode holen wir uns einfach den String-Wert der Nutzlast der Nachricht und führen einen Switch-Ausdruck aus, um den Wert der Nachricht mit dem Wert des jeweiligen Falls zu überprüfen. Eine eindeutige Nachricht mit dem Tiergeräusch wird an den Client gesendet.

Konfigurieren Sie die WebSocket-Anfrageverarbeitung

Implementieren Sie in WebSocketConfiguration.java die Schnittstelle WebSocketConfigurer und fügen Sie den folgenden Code in die Datei ein:

package com.server.javawebsocketserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new WebSocketHandler(), "/websocket");
    }
}
Enter fullscreen mode Exit fullscreen mode

Wir richten die Methode registerWebSocketHandlers ein, um den WebSocketHandler auf den Pfad "/websocket" zu konfigurieren. Das ist alles für die Server-Seite. Nun, da wir alles eingerichtet haben, können wir den WebSocket-Server starten und die Anwendung ausführen!

Daten zwischen Server und Client senden

Wechseln Sie im Terminal in das Stammverzeichnis Ihres Spring Boot-Projekts und führen Sie den folgenden Befehl aus, um den Server zu starten:

gradle bootRun
Enter fullscreen mode Exit fullscreen mode

Führen Sie als Nächstes den Android-Client in Android Studio aus, und sobald die App geladen ist, klicken Sie auf eine der vier Schaltflächen.

Spielen Sie mit der Android-App herum und sehen Sie, wie Nachrichten mit WebSockets vom Client zum Server gesendet werden!

Aktualisieren Sie den Android-Client auf Pub/Sub

Das Senden von Daten von Client zu Server oder von Server zu Client ist nicht schwierig und kann ziemlich schnell erledigt werden. Was aber, wenn Sie Daten von Client zu Client senden möchten? Sie können Clients nicht direkt miteinander verbinden, ohne eine Routing- und Message-Broker-Logik auf dem Server zu implementieren (über enableSimpleBroker und die Registrierung beim MessageBrokerRegistry-Typ ).

Es gibt mehrere Tools, die diese Aufgabe weniger zeitaufwändig machen. Ein solches Tool ist Socket.IO, das eine bidirektionale Echtzeitverbindung zwischen Clients herstellt. Dies ist ein großartiges Open-Source-Tool, aber wir müssen trotzdem einen Server einrichten und den Client mit dem Server verbinden. Ein weiteres solches Tool ist die Verwendung von Unterprotokollen wie STOMP in Spring Boot, um STOMP-Nachrichten einzubetten. Dieses Unterprotokoll setzt auf WebSocket auf und ermöglicht die Verarbeitung von WebSocket-Nachrichten. Allerdings müssen Sie auch die Endpunkte selbst einrichten, indem Sie den Typ StompEndpointRegistry verwenden und dann die Endpunkte mit der Methode addEndPoint hinzufügen. Gibt es einen einfacheren Weg, um Daten sicher und zuverlässig zwischen Clients zu senden, ohne manuell einen Server einzurichten? Mit PubNub können wir das.

PubNub bietet die Echtzeit-Infrastruktur, um jedes TCP-fähige Gerät zu betreiben. Wir können Daten von Client zu Client, von Client zu Server oder von Server zu Client mit PubNub's Global Data Stream Network in weniger als 100 ms übertragen! Mit PubNub wird eine permanente Verbindung zwischen den Geräten hergestellt, die mit dem Kanal verbunden sind, ähnlich wie bei WebSockets. Das Beste daran ist, dass Sie sich nicht um die Einrichtung eines Backend-Servers und die Wartung des Servers kümmern müssen, da PubNub serverlos und unendlich skalierbar ist.

Um zu sehen, wie PubNub den Prozess des Sendens von Daten von Client zu Client vereinfacht, werden wir die Android-App, die wir zuvor gebaut haben, modifizieren. Aber zuerst müssen Sie sich für ein kostenloses PubNub-Konto anmelden, um Ihre kostenlosen Pub/Sub-API-Schlüssel zu erhalten.

Ändern Sie den Android-Client

Um die aktualisierte App von der alten App zu unterscheiden, erstellen Sie ein neues Android-Projekt namens PubNubJavaClient. Um das Android-SDK von PubNub zu verwenden, aktualisieren Sie die json-Datei, indem Sie Folgendes zur build.gradle-Datei im App-Verzeichnis hinzufügen und das Projekt synchronisieren:

dependencies {
    implementation group: 'com.pubnub', name: 'pubnub-gson', version: '6.4.5' // Add this
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    ...
}
Enter fullscreen mode Exit fullscreen mode

Fügen Sie die folgenden Berechtigungen in die Manifestdatei ein:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Enter fullscreen mode Exit fullscreen mode

Alles andere, außer MainActivity.java, bleibt gleich. Fügen Sie der aktualisierten App die folgenden Dateien aus der vorherigen App hinzu: animal_sounds.xml, strings.xml und die Bilder aus dem drawable-Verzeichnis. Sobald Sie damit fertig sind, fügen Sie in MainActivity.java den neuen Code ein. Importieren Sie die folgenden Pakete und richten Sie onCreate() ein:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.pubnub.api.PNConfiguration;
import com.pubnub.api.PubNub;
import com.pubnub.api.callbacks.PNCallback;
import com.pubnub.api.callbacks.SubscribeCallback;
import com.pubnub.api.models.consumer.PNPublishResult;
import com.pubnub.api.models.consumer.PNStatus;
import com.pubnub.api.models.consumer.pubsub.PNMessageResult;
import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
  PubNub pubnub;
  TextView textView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.animal_sounds);

    initPubNub(); // Initialize PubNub
  }
Enter fullscreen mode Exit fullscreen mode

Rufen Sie initPubNub() auf, um PubNub zu initialisieren:

public void initPubNub(){
  PNConfiguration pnConfiguration = new PNConfiguration();
  pnConfiguration.setPublishKey("ENTER_YOUR_PUB_KEY"); // REPLACE with your pub key
  pnConfiguration.setSubscribeKey("ENTER_YOUR_SUB_KEY"); // REPLACE with your sub key
  pnConfiguration.setSecure(true);
  pubnub = new PubNub(pnConfiguration);

  // Listen to messages that arrive on the channel
  pubnub.addListener(new SubscribeCallback() {
    @Override
    public void status(PubNub pub, PNStatus status) {
    }

    @Override
    public void message(PubNub pub, final PNMessageResult message) {
      // Replace double quotes with a blank space
      final String msg = message.getMessage().toString().replace("\"", ""); 
      textView = findViewById(R.id.animalSound);

      runOnUiThread(new Runnable() {
        @Override
        public void run() {
          try{
            // Display the message on the app
            textView.setText(msg);
          } catch (Exception e){
              System.out.println("Error");
              e.printStackTrace();
          }
        }
      });
    }

    @Override
    public void presence(PubNub pub, PNPresenceEventResult presence) {
    }
  });

  // Subscribe to the global channel
  pubnub.subscribe()
    .channels(Arrays.asList("global_channel"))
    .execute();
}
Enter fullscreen mode Exit fullscreen mode

In dieser Methode erledigen wir drei wichtige Dinge:

  1. Initialisierung der PubNub-Client-API. Stellen Sie sicher, dass Sie "ENTER_YOUR_PUB_KEY" und "ENTER_YOUR_SUB_KEY" durch Ihre Pub/Sub-Schlüssel ersetzen.

  2. Richten Sie einen Listener ein, um über Nachrichten, die auf dem Kanal ankommen, informiert zu werden. Wie zuvor zeigen Sie die Nachricht in der App an, damit der Client sie sehen kann.

  3. Abonnieren Sie den globalen Kanal, in dem die Nachrichten veröffentlicht werden sollen.

Wenn der Benutzer eine Schaltfläche drückt, wird die Methode sendMessage() aufgerufen:

// This method is called when a button is pressed
public void sendMessage(View view) {
  // Get button ID
  switch(view.getId()){
    case(R.id.dogButton):
      publishMessage("Woooof");
      break;

    case(R.id.catButton):
      publishMessage("Meooow");
      break;

    case(R.id.pigButton):
      publishMessage("Bork Bork");
      break;

    case(R.id.foxButton):
      publishMessage("Fraka-kaka-kaka");
      break;
  }
}
Enter fullscreen mode Exit fullscreen mode

Diese Methode ähnelt dem, was wir zuvor gemacht haben, nur dass wir jetzt die eigentliche Nachricht, den Tierlaut, im globalen Kanal und nicht auf dem Server veröffentlichen. Wir verwenden publishMessage() als Hilfsfunktion, um die Nachricht zu veröffentlichen.

public void publishMessage(String animal_sound){
  // Publish message to the global chanel
  pubnub.publish()
    .message(animal_sound)
    .channel("global_channel")
    .async(new PNCallback<PNPublishResult>() {
      @Override
      public void onResponse(PNPublishResult result, PNStatus status) {
        // status.isError() to see if error happened and print status code if error
        if(status.isError()) {
          System.out.println("pub status code: " + status.getStatusCode());
        }
      }
    });
}
Enter fullscreen mode Exit fullscreen mode

Das ist alles, was wir brauchen, um die Anwendung zum Laufen zu bringen!

Daten zwischen Clients senden

Führen Sie die Android-App in zwei Emulatoren aus, um zu sehen, wie die Nachrichten in Echtzeit erscheinen.

Hinweis: Um eine nahtlose Kommunikation zwischen den Clients zu gewährleisten, stellen Sie sicher, dass beide Android-Emulatoren dieselben PubNub-Schlüssel haben und am selben Kanal angemeldet sind. Dies ist entscheidend für den Aufbau einer erfolgreichen WebSocket-Verbindung für die Echtzeitkommunikation.

Nun, da Sie gelernt haben, wie man Daten mit WebSockets in Java sendet, vergessen Sie nicht, unsere anderen Ressourcen zu erkunden.

Schauen Sie sich unsere Dokumente an, um mehr über die zusätzlichen Funktionen zu erfahren, die PubNub bietet, wie z.B. Message Actions, Objects und Signal Messages, um Ihrer Anwendung einen robusten Vorteil zu verschaffen. Sehen Sie sich unsere aktualisierten Screenshots und Ressourcenlinks an, um ein umfassenderes Verständnis zu erhalten.

Mit PubNub können Sie auch maßgeschneiderte Lösungen für Geolocation, Edge Message Bus, Unternehmenssoftware, Gaming, Digital Health und E-Commerce erstellen.

Für den Einstieg empfiehlt sich unser allgemeiner Einrichtungsleitfaden, der Sie durch die Einrichtung Ihres Kontos führt. Wenn Sie wissen möchten, wie Sie Nachrichten senden und empfangen können, haben wir auch dafür Anleitungen. Weitere Informationen finden Sie in unseren Anleitungen zum Senden und Empfangen von Nachrichten.

Für fortgeschrittene Funktionen, wie das Senden von Nachrichten, Nachrichtenbestätigungen und die Anzahl der ungelesenen Nachrichten in einem Chat-Szenario, können Sie sich auf diese Dokumente beziehen: Senden von Nachrichten, Nachrichtenbestätigungen und Anzahl der ungelesenen Nachrichten.

Wenn Sie auf einer Android-Plattform arbeiten und Push-Benachrichtigungen implementieren möchten, haben wir für Sie das Richtige. In unserem Android-Push-Guide und Send-Push-Guide finden Sie ausführliche Anweisungen zur Einrichtung von Push-Benachrichtigungen mit Firebase und PubNub.

Haben Sie Vorschläge oder Fragen zum Inhalt dieses Beitrags? Wenden Sie sich an devrel@pubnub.com.

Wie kann PubNub Ihnen helfen?

Dieser Artikel wurde ursprünglich auf PubNub.com veröffentlicht.

Unsere Plattform unterstützt Entwickler bei der Erstellung, Bereitstellung und Verwaltung von Echtzeit-Interaktivität für Webanwendungen, mobile Anwendungen und IoT-Geräte.

Die Grundlage unserer Plattform ist das größte und am besten skalierbare Echtzeit-Edge-Messaging-Netzwerk der Branche. Mit über 15 Points-of-Presence weltweit, die 800 Millionen monatlich aktive Nutzer unterstützen, und einer Zuverlässigkeit von 99,999 % müssen Sie sich keine Sorgen über Ausfälle, Gleichzeitigkeitsgrenzen oder Latenzprobleme aufgrund von Verkehrsspitzen machen.

PubNub erleben

Sehen Sie sich die Live Tour an, um in weniger als 5 Minuten die grundlegenden Konzepte hinter jeder PubNub-gestützten App zu verstehen

Einrichten

Melden Sie sich für einen PubNub-Account an und erhalten Sie sofort kostenlosen Zugang zu den PubNub-Schlüsseln

Beginnen Sie

Mit den PubNub-Dokumenten können Sie sofort loslegen, unabhängig von Ihrem Anwendungsfall oder SDK

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .