Java WebSocket Programming with Android and Spring Boot
Published: 2024-01-17 02:18:39

The [WebSocket protocol](https://www.pubnub.com/blog/websockets-vs-rest-api-understanding-the-difference/) provides an always-on connection between a client and a server for bi-directional communication. This is great for applications that require a real-time connection, such as [multiplayer games](https://www.pubnub.com/industry/gaming/), [internet of things applications](https://www.pubnub.com/industry/iot-and-connected-devices/), and [chat apps](https://www.pubnub.com/use-case/in-app-chat/). The use case for this tutorial is a chat application, a classic example of real-time communication. In this tutorial, we are going to set up a simple Android client that will connect to a WebSocket server using [Spring Boot](https://spring.io/projects/spring-boot).

![](/cdn/3prze68gbwl1/archivedasset-17su7vkk2ohxnb3/97e1b1e1ab1a6f2f7de233157b9e0c31/WebSockets-Diagram.png)

Java WebSocket Client
---------------------

For the Android client, we are going to make a simple demo app that contains four image buttons of cute animals. Before diving into the Java implementation it is worth noting that Kotlin has become a popular choice for Android development. To get started, initialize a new project on [Android Studio](https://developer.android.com/studio), with a Basic Activity, called **JavaWebSocketClient**. We are going to use a lightweight WebSocket client library for the app, which can be found in this GitHub [repo](https://github.com/gusavila92/java-android-websocket-client). In order to use this library, we have to add it to the **build.gradle** file in the app directory. Add the following to the dependencies and sync the project:

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

Make sure to include the internet access permission in the manifest file:

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

### Connect the Client to the Server

Go to **MainActivity.java**, import the following packages and set up **onCreate()**:

```js
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();
  }
}
```

Next, create a new method **createWebSocketClient()**:

```js
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();
  }
```

This may look like a lot, but really, we are doing four key things in this method:

1.  Starting a new WebSocket connection to the localhost “ws://10.0.2.2:8080/websocket”.
    
2.  Sending a message to the server once a connection is opened.
    
3.  Displaying the messages sent from the server on the app.
    
4.  Setting timeouts and automatic reconnection.
    

Now that we connected the client to the server, let’s set up the method to send messages to the server.

### Send Messages to the Server

In **MainActivity.java**, add the following to **sendMessage()**:

```js
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;
   }
 }
```

When a button is pressed, the _button id_ is sent to the server. This method is called from the file **animal\_sounds.xml**, which you can get from my [Java WebSocket Programming Repo](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/layout/animal_sounds.xml). Make sure to modify the file [**strings.xml**](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/values/strings.xml) under the **values** directory so you won’t get any errors in the XML file.

The last thing to do on the client-side is to add the images for the animals in the [**drawable**](https://github.com/ocastroa/java-websocket-demo/tree/master/JavaWebSocketClient/app/src/main/res/drawable) directory. _The images are made by_ [_Freepik_](https://www.flaticon.com/authors/freepik) _from_ [_www.flaticon.com_](https://www.flaticon.com/)_._

Run the Android app in your emulator:

![](/cdn/3prze68gbwl1/asset-17suaysk1qa1k1d/c3da7192b47d3ca4e89d9012ad20fa62/clicking_buttons_client.gif?w=700&h=550)

Nothing happens right now because the server is not set up. Let’s do that right now!

Spring Boot WebSocket Server
----------------------------

For our server, we will use Spring Boot which makes it easy to create production-grade [Spring](https://spring.io/projects/spring-framework) applications with minimum configurations. To quickly bootstrap our project, we will use [Spring Initializr](https://start.spring.io/). We will generate a [Gradle](https://gradle.org/) project, but you can also generate a [Maven](https://maven.apache.org/) project if you prefer. Configure the Initializr like the screenshot below and make sure to add WebSocket as a dependency:

![](/cdn/3prze68gbwl1/asset-17suaysk1qa1k1e/3a66f9b5a0a6dcdee6f40f92a45d59fc/spring_initializr-1.png)

Generate the project to download a zip file. Once you unzip the file, go to the **src** directory and keep on clicking the subdirectories until you get to the **JavaWebSocketServerApplication.java** file.

```js
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);
  }
}
```

Add two files to the directory **WebSocketHandler.java** and **WebSocketConfiguration.java.**

### Handle the WebSocket Messages

We have to handle the incoming messages that arrive in the server. To do so, in **WebSocketHandler.java** inherit the class to implement the method **handleTextMessage()**. Add the following code to the file:

```js
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");
        }
    }
}
```

Inside the method, we simply get the string value of the message payload and do a **switch** expression to check the value of the message with the value of each case. A unique message with the animal sound is sent to the client.

### Configure the WebSocket Request Handling

In **WebSocketConfiguration.java**, implement the interface [**WebSocketConfigurer**](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/socket/config/annotation/WebSocketConfigurer.html) and add the following code to the file:

```js
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");
    }
}
```

We set up the method **registerWebSocketHandlers** to configure the [WebSocketHandler](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/socket/config/annotation/WebSocketConfigurer.html#addHandler-org.springframework.web.socket.WebSocketHandler-java.lang.String...-) to the path “**/websocket**“. That is all for the server-side. Now that we have everything set up, let’s start the WebSocket server and run the app!

Send Data between Server and Client
-----------------------------------

In the terminal, go to the root directory of your Spring Boot project and run the following command to start the server:

```js
gradle bootRun
```

Next, run the Android client in Android Studio, and once the app loads, click any of the four buttons.

![](/cdn/3prze68gbwl1/asset-17suaysk1qa1k1f/6153c8947488b515b3166179789f8562/client_server_screenshot.png)

Play around with the Android app and see how messages are sent from client to server with WebSockets!

Update the Android Client to Pub/Sub
------------------------------------

Sending data client-to-server or server-to-client is not difficult and can be done pretty fast. But what if you want to send data client-to-client? You can’t directly connect clients without implementing some routing and message broker logic (via `enableSimpleBroker` and registering it with `MessageBrokerRegistry` type) on the server.

There are several tools we can use to make this task less time-consuming. One such tool is [Socket.IO](http://socket.io/), which sets up a real-time, bidirectional connection between clients. This is a great open-source tool to use, but we still need to set up a server and connect the client to the server. Another such tool is to use subprotocols like [STOMP](https://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol) in Spring Boot to embed STOMP messages. This subprotocol operates on top of WebSocket and enables WebSocket message handling. However, you also have to set up the endpoints yourself using the `StompEndpointRegistry` type and then add the endpoints with the `addEndPoint` method. Is there an easier way to securely and reliably send data between clients without manually setting up a server? With PubNub, we can.

![](/cdn/3prze68gbwl1/asset-17suaysk1qa1k1g/7e74251d4425718e24dd4c587e849ca5/pubsub-1.gif?w=700&h=550)

PubNub provides the real-time infrastructure to power any device that speaks [TCP](https://www.pubnub.com/guides/tcp-ip/). We can stream data from client-to-client, client-to-server, or server-to-client using [PubNub’s Global Data Stream Network](https://www.pubnub.com/products/pubnub-platform/) in under 100 ms! With PubNub, an always-on connection is made between the devices connected to the channel, similar to WebSockets. The best part is that you don’t have to worry about setting up a backend server and maintaining the server because PubNub is [serverless](https://www.pubnub.com/blog/everything-you-wanted-to-know-about-serverless-but-were-afraid-to-ask/) and infinitely scalable.

To see how PubNub simplifies the process of sending data from client-to-client, we will modify the Android app we built earlier. But first, [sign up](https://admin.pubnub.com/#/signup) for a free PubNub account to get your **free Pub/Sub API keys**.

### Modify the Android Client

To differentiate the updated app from the old app, create a new Android project called **PubNubJavaClient**. In order to use [PubNub’s Android SDK](https://www.pubnub.com/docs/sdks/java/android/), update the json by adding the following to the build.gradle file in the app directory and sync the project:

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

Include the following permissions to the manifest file:

```js
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

Everything else, except **MainActivity.java**, is the same. From the previous app, add the following files to the updated app:  [**animal\_sounds.xml**](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/layout/animal_sounds.xml), [**strings.xml**](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/values/strings.xml) and the images from the [**drawable**](https://github.com/ocastroa/java-websocket-demo/tree/master/JavaWebSocketClient/app/src/main/res/drawable) directory. Once you finish this, go to **MainActivity.java** to add the new code. Import the following packages and set up **onCreate()**:

```js
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
  }
```

Make a call to **initPubNub()** to initialize PubNub:

```js
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();
}
```

We do three key things in this method:

1.  Initialize the PubNub client API. Make sure to replace “ENTER\_YOUR\_PUB\_KEY” and “ENTER\_YOUR\_SUB\_KEY” with your Pub/Sub keys.
    
2.  Set up a listener to get notified of messages that arrive on the channel. As we did earlier, display the message on the app for the client to see.
    
3.  Subscribe to the global channel where messages will be published.
    

When the user presses a button, the method **sendMessage()** is called:

```js
// 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;
  }
}
```

This method is similar to what we did earlier, except now we publish the actual message, the animal sound, to the global channel and not the server. We use **publishMessage()** as a helper function to publish the message.

```js
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());
        }
      }
    });
}
```

That is all we need to get the app up and running!

Send Data Between Clients
-------------------------

Run the Android app in two emulators to see messages appear in real time.

![](/cdn/3prze68gbwl1/asset-17suaysk1qa1k1h/b19bdcfbe68755a7d419c30f9281286c/client_to_client.gif?w=700&h=550)

Note: To ensure seamless communication between clients, make sure both the Android emulators have the same PubNub keys and are subscribed to the same channel. This is crucial for establishing a successful WebSocket connection for real-time communication.

Now that you've learned how to send data using WebSockets in Java, don't forget to explore our other resources.

Lastly, check out our docs to learn about additional features that PubNub offers such as [Message Actions](https://www.pubnub.com/docs/chat/sdks/messages/sending-messages), [Objects](https://www.pubnub.com/docs/general/setup/users-and-devices), and [Signal messages](https://www.pubnub.com/docs/general/basics/send-messages) to give your application a robust edge. Check out our updated screenshots and resource links for a more comprehensive understanding.

With PubNub, you can also create solutions tailored for [geolocation](https://www.pubnub.com/solutions/geolocation/), [edge message bus](https://www.pubnub.com/solutions/edge-message-bus/), [enterprise software](https://www.pubnub.com/solutions/enterprise-software/), [gaming](https://www.pubnub.com/solutions/gaming/), [digital health](https://www.pubnub.com/solutions/digital-health/), and [ecommerce](https://www.pubnub.com/solutions/ecommerce/).

To get started, you might want to check our [general setup guide](https://www.pubnub.com/docs/general/basics/set-up-your-account), which will walk you through setting up your account. If you're interested in learning how to send and receive messages, we have guides for that too. Check out our [send messages guide](https://www.pubnub.com/docs/general/basics/send-messages) and [receive messages guide](https://www.pubnub.com/docs/general/basics/receive-messages) for more information.

For more advanced functionalities, such as sending messages, message receipts, and unread counts in a chat scenario, you may refer to these documents: [sending messages](https://www.pubnub.com/docs/chat/sdks/messages/sending-messages), [message receipts](https://www.pubnub.com/docs/chat/sdks/messages/message-receipts), and [unread counts](https://www.pubnub.com/docs/chat/sdks/messages/unread-counts).

If you're working on an Android platform and want to implement push notifications, we've got you covered. Visit our [Android push guide](https://www.pubnub.com/docs/general/push/android) and [send push guide](https://www.pubnub.com/docs/general/push/send) for detailed instructions on how to set up push notifications with firebase and PubNub.

Have suggestions or questions about the content of this post? Reach out to [devrel@pubnub.com](mailto:devrel@pubnub.com).

How can PubNub help you?
========================

This article was originally published on [PubNub.com](https://www.pubnub.com/blog/java-websocket-programming-with-android-and-spring-boot/)

Our platform helps developers build, deliver, and manage real-time interactivity for web apps, mobile apps, and IoT devices.

The foundation of our platform is the industry's largest and most scalable real-time edge messaging network. With over 15 points-of-presence worldwide supporting 800 million monthly active users, and 99.999% reliability, you'll never have to worry about outages, concurrency limits, or any latency issues caused by traffic spikes.

Experience PubNub
-----------------

Check out [Live Tour](https://www.pubnub.com/tour/introduction/) to understand the essential concepts behind every PubNub-powered app in less than 5 minutes

Get Setup
---------

Sign up for a [PubNub account](https://admin.pubnub.com/signup/) for immediate access to PubNub keys for free

Get Started
-----------

The [PubNub docs](https://www.pubnub.com/docs) will get you up and running, regardless of your use case or [SDK](https://www.pubnub.com/docs)