AndroidとSpring BootによるJava WebSocketプログラミング
Published: 2024-01-17 02:41:03

[WebSocketプロトコルは](https://www.pubnub.com/blog/websockets-vs-rest-api-understanding-the-difference/)、クライアントとサーバー間の双方向通信のための常時接続を提供します。これは、[マルチプレイヤーゲーム](https://www.pubnub.com/industry/gaming/)、[モノのインターネットアプリケーション](https://www.pubnub.com/industry/iot-and-connected-devices/)、[チャットアプリなど](https://www.pubnub.com/use-case/in-app-chat/)、リアルタイム接続を必要とするアプリケーションに最適です。このチュートリアルのユースケースは、リアルタイム通信の典型的な例であるチャット・アプリケーションです。このチュートリアルでは、[Spring Bootを使って](https://spring.io/projects/spring-boot)WebSocketサーバーに接続するシンプルなAndroidクライアントをセットアップします。

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

Java WebSocketクライアント
--------------------

Androidクライアントでは、かわいい動物の4つの画像ボタンを含む簡単なデモアプリを作成します。Javaの実装に入る前に、KotlinがAndroidの開発で人気のある選択肢になっていることに注目する価値がある。まず、[Android Studioで](https://developer.android.com/studio)新しいプロジェクトを初期化し、**JavaWebSocketClientという**基本アクティビティを作成します。このアプリには、GitHubの[レポに](https://github.com/gusavila92/java-android-websocket-client)ある軽量なWebSocketクライアント・ライブラリを使用します。このライブラリを使うには、appディレクトリの**build.gradle**ファイルに追加する必要がある。依存関係に以下を追加し、プロジェクトを同期する：

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

マニフェスト・ファイルにインターネット・アクセス許可を必ず含めること：

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

### クライアントをサーバーに接続する

**MainActivity.javaに**移動し、以下のパッケージをインポートし、**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();
  }
}
```

次に、新しいメソッド**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();
  }
```

これはたくさんあるように見えるかもしれませんが、実際にはこのメソッドで4つの重要なことを行っています：

1.  ローカルホスト "ws://10.0.2.2:8080/websocket" への新しい WebSocket 接続を開始する。
    
2.  接続が開かれたら、サーバーにメッセージを送信する。
    
3.  サーバーから送信されたメッセージをアプリに表示する。
    
4.  タイムアウトと自動再接続を設定する。
    

クライアントをサーバーに接続したところで、サーバーにメッセージを送信するメソッドを設定しましょう。

### サーバーへのメッセージ送信

**MainActivity.javaの** **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;
   }
 }
```

ボタンが押されたら、その_ボタンIDを_ サーバーに送信する。このメソッドは**animal\_sounds.xmlから**呼び出されます。an**imal\_sounds**.xmlは私の[Java WebSocket Programming Repoから](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/layout/animal_sounds.xml)入手できます。**values**ディレクトリの下にある[**strings.xml**](https://github.com/ocastroa/java-websocket-demo/blob/master/JavaWebSocketClient/app/src/main/res/values/strings.xml)ファイルを必ず修正して、XMLファイルにエラーが出ないようにしてください。

クライアント側で行う最後のことは、[**drawable**](https://github.com/ocastroa/java-websocket-demo/tree/master/JavaWebSocketClient/app/src/main/res/drawable)ディレクトリに動物の画像を追加することです。_画像は_ 、[_www.flaticon.com_](https://www.flaticon.com/) _、_ [_Freepikによって_](https://www.flaticon.com/authors/freepik) _作成さ_ れました_。_

Androidアプリをエミュレーターで実行します：

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

サーバーがセットアップされていないので、今は何も起こりません。今すぐ実行しましょう！

Spring Boot WebSocketサーバー
-------------------------

サーバーにはSpring Bootを使う。プロジェクトを素早くブートストラップするために、[Spring Initializrを](https://start.spring.io/)使う。ここでは[Gradle](https://gradle.org/)プロジェクトを生成するが、必要に応じて[Maven](https://maven.apache.org/)プロジェクトを生成することもできる。以下のスクリーンショットのようにInitializrを設定し、依存関係としてWebSocketを追加してください：

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

プロジェクトを生成してzipファイルをダウンロードする。ファイルを解凍したら、**src**ディレクトリに移動し、**JavaWebSocketServerApplication.java**ファイルに到達するまでサブディレクトリをクリックし続けます。

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

**WebSocketHandler.javaと** **WebSocketConfiguration.javaの**2つのファイルをディレクトリに追加します。

### WebSocket メッセージの処理

サーバーに到着した受信メッセージを処理する必要があります。そのために、**WebSocketHandler.java** で**handleTextMessage()** メソッドを実装するクラスを継承します。このファイルに以下のコードを追加します：

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

メソッド内部では、単純にメッセージのペイロードの文字列値を取得し、各ケースの値とメッセージの値をチェックする**switch**式を実行します。動物の鳴き声が入ったユニークなメッセージがクライアントに送信されます。

### WebSocket リクエスト処理の設定

**WebSocketConfiguration.java** で[**WebSocketConfigurer**](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/socket/config/annotation/WebSocketConfigurer.html) インターフェースを実装し、以下のコードを追加します：

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

**registerWebSocketHandlers** メソッドを設定し、パス "**/websocket**" に[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...-) を設定します。サーバーサイドの設定は以上です。これですべての設定が完了したので、WebSocketサーバーを起動してアプリを実行してみましょう！

サーバーとクライアント間でデータを送信する
---------------------

ターミナルでSpring Bootプロジェクトのルートディレクトリに移動し、以下のコマンドを実行してサーバーを起動する：

```js
gradle bootRun
```

次に、Android StudioでAndroidクライアントを実行し、アプリがロードされたら、4つのボタンのいずれかをクリックします。

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

Androidアプリを操作して、WebSocketを使ってクライアントからサーバーにどのようにメッセージが送信されるかを確認する！

AndroidクライアントをPub/Subに更新する
--------------------------

クライアントからサーバーへ、またはサーバーからクライアントへデータを送信することは難しくありませんし、かなり速く行うことができます。しかし、クライアントからクライアントへデータを送信したい場合はどうすればいいでしょうか？サーバにルーティングとメッセージブローカのロジック（`enableSimpleBrokerと` `MessageBrokerRegistry`型への登録）を実装しない限り、クライアントを直接接続することはできません。

この作業に時間がかからないようにするために使えるツールがいくつかある。そのひとつが[Socket.IO](http://socket.io/) で、クライアント間でリアルタイムの双方向接続を確立します。これは素晴らしいオープンソースのツールだが、サーバーをセットアップし、クライアントをサーバーに接続する必要がある。もう1つのそのようなツールは、Spring Bootで[STOMPの](https://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol)ようなサブプロトコルを使ってSTOMPメッセージを埋め込むことだ。このサブプロトコルはWebSocketの上で動作し、WebSocketメッセージハンドリングを可能にする。しかし、`StompEndpointRegistry`型を使って自分でエンドポイントを設定し、`addEndPoint`メソッドでエンドポイントを追加しなければなりません。手動でサーバーをセットアップすることなく、クライアント間で安全かつ確実にデータを送信する簡単な方法はないだろうか？PubNubを使えば可能です。

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

PubNubは、[TCPを](https://www.pubnub.com/guides/tcp-ip/)話すあらゆるデバイスに電力を供給するリアルタイムインフラを提供します。[PubNubのグローバルデータストリームネットワークを](https://www.pubnub.com/products/pubnub-platform/)使用して、クライアント間、クライアントからサーバー間、サーバーからクライアント間のデータを100ミリ秒未満でストリーミングできます！PubNubでは、WebSocketと同様にチャネルに接続されたデバイス間で常時接続が行われます。PubNubは[サーバーレスで](https://www.pubnub.com/blog/everything-you-wanted-to-know-about-serverless-but-were-afraid-to-ask/)無限にスケーラブルなので、バックエンドサーバーのセットアップやサーバーのメンテナンスを心配する必要がないのが最大の特徴です。

PubNubによってクライアントからクライアントへのデータ送信プロセスがどのように簡素化されるかを確認するために、先に構築したAndroidアプリを修正する。その前に、無料のPubNubアカウントに[サインアップして](https://admin.pubnub.com/#/signup) **無料のPub/Sub APIキーを**入手してください。

### Androidクライアントの修正

更新したアプリを古いアプリと区別するために、**PubNubJavaClientという**新しいAndroidプロジェクトを作成します。[PubNubのAndroid SDKを](https://www.pubnub.com/docs/sdks/java/android/)使用するため、appディレクトリのbuild.gradleファイルに以下を追加してjsonを更新し、プロジェクトを同期します：

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

マニフェストファイルに次のパーミッションを含めます：

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

**MainActivity.java**以外は同じです。以前のアプリから、次のファイルを更新後のアプリに追加します：[**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)、および[**drawable**](https://github.com/ocastroa/java-websocket-demo/tree/master/JavaWebSocketClient/app/src/main/res/drawable)ディレクトリの画像。これが終わったら、**MainActivity.javaに** 新しいコードを追加します。以下のパッケージをインポートし、**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
  }
```

**initPubNub()**を呼び出して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();
}
```

このメソッドでは3つの重要なことを行います：

1.  PubNubクライアントAPIを初期化します。ENTER\_YOUR\_PUB\_KEY "と "ENTER\_YOUR\_SUB\_KEY "をあなたのPub/Subキーに置き換えてください。
    
2.  チャンネルに到着したメッセージの通知を受けるリスナーをセットアップします。先ほど行ったように、クライアントが見ることができるようにアプリにメッセージを表示します。
    
3.  メッセージがパブリッシュされるグローバルチャネルをサブスクライブする。
    

ユーザーがボタンを押すと、**sendMessage()** メソッドが呼び出されます：

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

このメソッドは先ほどと似ていますが、実際のメッセージである動物の鳴き声を、サーバーではなくグローバルチャンネルに公開する点が異なります。**publishMessage()**は、メッセージをパブリッシュするためのヘルパー関数です。

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

アプリを起動して実行するのに必要なのはこれだけだ！

クライアント間でデータを送信する
----------------

Androidアプリを2つのエミュレーターで実行し、メッセージがリアルタイムで表示されるのを確認してみましょう。

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

注意：クライアント間でシームレスな通信を行うには、両方のAndroidエミュレータが同じPubNubキーを持ち、同じチャネルにサブスクライブしていることを確認してください。これはリアルタイム通信のためのWebSocket接続を正常に確立するために非常に重要です。

JavaでWebSocketを使用してデータを送信する方法を学んだので、私たちの他のリソースを探索することを忘れないでください。

最後に、PubNubが提供する[Message Actions](https://www.pubnub.com/docs/chat/sdks/messages/sending-messages)、[Objects](https://www.pubnub.com/docs/general/setup/users-and-devices)、[Signalメッセージ](https://www.pubnub.com/docs/general/basics/send-messages)などの追加機能について学ぶために私たちのドキュメントをチェックして、あなたのアプリケーションに堅牢なエッジを与えてください。より包括的な理解のために、更新されたスクリーンショットとリソースリンクをチェックしてください。

PubNubを使用すると、[ジオロケーション](https://www.pubnub.com/solutions/geolocation/)、[エッジメッセージバス](https://www.pubnub.com/solutions/edge-message-bus/)、[エンタープライズソフトウェア](https://www.pubnub.com/solutions/enterprise-software/)、[ゲーム](https://www.pubnub.com/solutions/gaming/)、[デジタルヘルス](https://www.pubnub.com/solutions/digital-health/)、[eコマース](https://www.pubnub.com/solutions/ecommerce/)向けにカスタマイズされたソリューションを作成することもできます。

まずは、[一般的なセットアップガイドを](https://www.pubnub.com/docs/general/basics/set-up-your-account)ご覧ください。メッセージの送受信方法についてご興味のある方は、こちらもご覧ください。詳しくは、[メッセージ送信ガイドと](https://www.pubnub.com/docs/general/basics/send-messages) [メッセージ受信ガイドを](https://www.pubnub.com/docs/general/basics/receive-messages)ご覧ください。

チャットシナリオでのメッセージ送信、メッセージ受信、未読カウントなど、より高度な機能については、以下のドキュメントを参照してください：[メッセージ送信](https://www.pubnub.com/docs/chat/sdks/messages/sending-messages)、[メッセージ受信](https://www.pubnub.com/docs/chat/sdks/messages/message-receipts)、[未読カウント](https://www.pubnub.com/docs/chat/sdks/messages/unread-counts)。

Androidプラットフォームで作業していて、プッシュ通知を実装したい場合、私たちはあなたをカバーしています。firebaseとPubNubを使ったプッシュ通知の設定方法については[Androidプッシュガイドと](https://www.pubnub.com/docs/general/push/android) [送信プッシュガイドを](https://www.pubnub.com/docs/general/push/send)ご覧ください。

この記事の内容に関して提案や質問がありますか？[devrel@pubnub.com](mailto:devrel@pubnub.com) までご連絡ください。

PubNubはあなたのお役に立ちますか？
====================

この記事は[PubNub.com](https://www.pubnub.com/blog/java-websocket-programming-with-android-and-spring-boot/)に掲載されたものです。

私たちのプラットフォームは、開発者がWebアプリ、モバイルアプリ、およびIoTデバイスのためのリアルタイムのインタラクティブ性を構築、配信、管理するのに役立ちます。

私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。

PubNubを体験
---------

[ライブツアーを](https://www.pubnub.com/tour/introduction/)チェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ
------

[PubNubアカウントに](https://admin.pubnub.com/signup/)サインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める
---

[PubNubのドキュメントは](https://www.pubnub.com/docs)、ユースケースや[SDKに](https://www.pubnub.com/docs)関係なく、あなたを立ち上げ、実行することができます。