AndroidとSpring BootによるJava WebSocketプログラミング

PubNub Developer Relations - Jan 17 - - Dev Community

WebSocketプロトコルは、クライアントとサーバー間の双方向通信のための常時接続を提供します。これは、マルチプレイヤーゲームモノのインターネットアプリケーションチャットアプリなど、リアルタイム接続を必要とするアプリケーションに最適です。このチュートリアルのユースケースは、リアルタイム通信の典型的な例であるチャット・アプリケーションです。このチュートリアルでは、Spring Bootを使ってWebSocketサーバーに接続するシンプルなAndroidクライアントをセットアップします。

Java WebSocketクライアント

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

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

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

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

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

MainActivity.javaに移動し、以下のパッケージをインポートし、onCreate()を設定します:

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

次に、新しいメソッド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

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

  1. ローカルホスト "ws://10.0.2.2:8080/websocket" への新しい WebSocket 接続を開始する。

  2. 接続が開かれたら、サーバーにメッセージを送信する。

  3. サーバーから送信されたメッセージをアプリに表示する。

  4. タイムアウトと自動再接続を設定する。

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

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

MainActivity.javaの sendMessage()に以下を追加します:

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

ボタンが押されたら、そのボタンIDを サーバーに送信する。このメソッドはanimal_sounds.xmlから呼び出されます。an*imal_sounds.xmlは私のJava WebSocket Programming Repoから入手できます。values*ディレクトリの下にあるstrings.xmlファイルを必ず修正して、XMLファイルにエラーが出ないようにしてください。

クライアント側で行う最後のことは、drawableディレクトリに動物の画像を追加することです。画像はwww.flaticon.com Freepikによって 作成さ れました

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

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

Spring Boot WebSocketサーバー

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

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

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

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

WebSocket メッセージの処理

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

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

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

WebSocket リクエスト処理の設定

WebSocketConfiguration.javaWebSocketConfigurer インターフェースを実装し、以下のコードを追加します:

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

registerWebSocketHandlers メソッドを設定し、パス "/websocket" にWebSocketHandler を設定します。サーバーサイドの設定は以上です。これですべての設定が完了したので、WebSocketサーバーを起動してアプリを実行してみましょう!

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

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

gradle bootRun
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

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

PubNubは、TCPを話すあらゆるデバイスに電力を供給するリアルタイムインフラを提供します。PubNubのグローバルデータストリームネットワークを使用して、クライアント間、クライアントからサーバー間、サーバーからクライアント間のデータを100ミリ秒未満でストリーミングできます!PubNubでは、WebSocketと同様にチャネルに接続されたデバイス間で常時接続が行われます。PubNubはサーバーレスで無限にスケーラブルなので、バックエンドサーバーのセットアップやサーバーのメンテナンスを心配する必要がないのが最大の特徴です。

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

Androidクライアントの修正

更新したアプリを古いアプリと区別するために、PubNubJavaClientという新しいAndroidプロジェクトを作成します。PubNubのAndroid SDKを使用するため、appディレクトリのbuild.gradleファイルに以下を追加してjsonを更新し、プロジェクトを同期します:

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

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

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

MainActivity.java以外は同じです。以前のアプリから、次のファイルを更新後のアプリに追加します:animal_sounds.xmlstrings.xml、およびdrawableディレクトリの画像。これが終わったら、MainActivity.javaに 新しいコードを追加します。以下のパッケージをインポートし、onCreate()を設定します:

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

initPubNub()を呼び出してPubNubを初期化します:

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

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

  1. PubNubクライアントAPIを初期化します。ENTER_YOUR_PUB_KEY "と "ENTER_YOUR_SUB_KEY "をあなたのPub/Subキーに置き換えてください。

  2. チャンネルに到着したメッセージの通知を受けるリスナーをセットアップします。先ほど行ったように、クライアントが見ることができるようにアプリにメッセージを表示します。

  3. メッセージがパブリッシュされるグローバルチャネルをサブスクライブする。

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

// 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

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

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

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

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

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

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

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

最後に、PubNubが提供するMessage ActionsObjectsSignalメッセージなどの追加機能について学ぶために私たちのドキュメントをチェックして、あなたのアプリケーションに堅牢なエッジを与えてください。より包括的な理解のために、更新されたスクリーンショットとリソースリンクをチェックしてください。

PubNubを使用すると、ジオロケーションエッジメッセージバスエンタープライズソフトウェアゲームデジタルヘルスeコマース向けにカスタマイズされたソリューションを作成することもできます。

まずは、一般的なセットアップガイドをご覧ください。メッセージの送受信方法についてご興味のある方は、こちらもご覧ください。詳しくは、メッセージ送信ガイドと メッセージ受信ガイドをご覧ください。

チャットシナリオでのメッセージ送信、メッセージ受信、未読カウントなど、より高度な機能については、以下のドキュメントを参照してください:メッセージ送信メッセージ受信未読カウント

Androidプラットフォームで作業していて、プッシュ通知を実装したい場合、私たちはあなたをカバーしています。firebaseとPubNubを使ったプッシュ通知の設定方法についてはAndroidプッシュガイドと 送信プッシュガイドをご覧ください。

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

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

この記事はPubNub.comに掲載されたものです。

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

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

PubNubを体験

ライブツアーをチェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ

PubNubアカウントにサインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める

PubNubのドキュメントは、ユースケースやSDKに関係なく、あなたを立ち上げ、実行することができます。

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