メインコンテンツまでスキップ

Android WebSocket の例

WebSocket は、クライアントとサーバー間で双方向の対話型通信セッションを開くことを可能にするプロトコルです。 単一の TCP 接続上で全二重通信チャネルを提供します。

このプロトコルは、Web ブラウザとサーバーの間に「ソケット」接続を確立する API を定義します。 たとえばライブスコア アプリから更新を取得するために長時間ポーリングするのではなく、WebSocket を使用してクライアントとサーバーの間に永続的な接続を作成できます。 したがって、新しいアップデートが利用可能になると、アプリ自体が自動更新されます。

これにより、新しい更新があるかどうかを確認するために追加の HTTP リクエストを送信するオーバーヘッドが回避されます。

したがって、クライアントとサーバーはハンドシェイクを完了するだけでよく、永続的な接続が両者の間に直接作成され、双方向のデータ転送が実行されます。

![Websocket](https://camo.githubusercontent.com/a985b5f6ece41fbd5ebcf5e126ac275cc0aab8f1d92c09696421f4c40e685f8a/68747470733a2f2f696d672d626c6f672e6373646e 696d672e636e2f32303139303130373130353635383936322e706e673f782d6f73732d70726f636573733d696d6167652f77617465726d61726b2c74797 0655f5a6d46755a33706f5a57356e6147567064476b2c736861646f775f31302c746578745f6148523063484d364c7939696247396e4c6d4e7a5a473475 626d56304c325a7662576c755833706f64513d3d2c73697a655f31362c636f6c6f725f4646464646462c745f3730)

Websocket は HTTP と同じ TCP ポートを使用するため、ほとんどのファイアウォール制限を回避できます。 デフォルトでは、Websocket プロトコルはポート 80 を使用します。 TLS 上で実行する場合、デフォルトでポート 443 が使用されます。 そのプロトコル識別子は ws です。 暗号化に wss が使用される場合、サーバーの Web アドレスは次のような URL になります。

ws://www.example.com/
wss://www.example.com/

Websocket の利点

  • 制御オーバーヘッドが少ない: 接続の作成後、サーバーとクライアントの間でデータが交換されるときに、プロトコル制御に使用されるデータ パケット ヘッダーは比較的小さいです。
  • より強力なリアルタイム パフォーマンス: プロトコルは全二重であるため、サーバーはいつでもアクティブにデータをクライアントに送信できます。
  • 接続状態を維持する: HTTP とは異なり、Websocket は最初に接続を作成する必要があるため、ステートフル プロトコルとなり、通信時に状態情報の一部を省略できます。 HTTP リクエストでは、各リクエストにステータス情報 (ID 認証など) を含める必要がある場合があります。
  • バイナリ サポートの向上: Websocket はバイナリ フレームを定義し、HTTP よりも簡単にバイナリ コンテンツを処理できます。
  • 優れた圧縮効果: HTTP 圧縮と比較して、Websocket は適切な拡張サポートを使用して以前のコンテンツのコンテキストを使用できるため、同様のデータを転送する際の圧縮率を大幅に向上できます。

例 1: Android WebSocket の例

このチュートリアルでは、OkHTTP を使用して WebSocket を使用する方法を学習します。 URL (ws://echo.websocket.org) は、WebSocket のセットアップに使用されます。

ステップ 1: OKhttp をインストールする

アプリレベルの build.gradle に次の実装ステートメントを追加します。

implementation 'com.squareup.okhttp3:okhttp:3.6.0'

ステップ 2: インターネット許可を追加する

Android マニフェストに、次のようにインターネット許可を追加します。

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

ステップ 3: レイアウトを設計する

テキストビューとボタンを含むレイアウトを作成します。 テキストビューにはサーバーからの結果が表示されます。 一方、ボタンは接続を開始します。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/buttonSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="SEND"
android:layout_marginTop="60dp"
android:textSize="20sp"/>
<TextView
android:id="@+id/textResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/buttonSend"
android:layout_centerHorizontal="true"
android:textSize="18sp"
android:layout_marginTop="40dp"/>
</RelativeLayout>

ステップ 4: コードを書く

まず、サーバーからの結果をテキストビューに出力するヘルパー メソッドを作成します。 これは UI スレッドで行われます。

    private void print(final String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
textResult.setText(textResult.getText().toString() + "\n" + message);
}
});
}

EchoWebListener を内部クラスとして作成します。 このクラスは WebSocketListener を拡張します。

    private final class EchoWebSocketListener extends WebSocketListener {
private static final int CLOSE_STATUS = 1000;
@Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("What's up ?");
webSocket.send(ByteString.decodeHex("abcd"));
webSocket.close(CLOSE_STATUS, "Socket Closed !!");
}
@Override
public void onMessage(WebSocket webSocket, String message) {
print("Receive Message: " + message);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
print("Receive Bytes : " + bytes.hex());
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(CLOSE_STATUS, null);
print("Closing Socket : " + code + " / " + reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable throwable, Response response) {
print("Error : " + throwable.getMessage());
}
}

次のメソッドは WebSocket 接続を開始します。 OkHTTP Request クラスがインスタンス化され、URL が url() メソッドに渡されます。 次に、EchoWebSocketListener をインスタンス化し、Request オブジェクトと EchoWebListener インスタンスの両方を newWebSocket() メソッドに渡します。

完全なメソッドは次のとおりです。

    private void start() {
Request request = new Request.Builder().url("ws://echo.websocket.org").build();
EchoWebSocketListener listener = new EchoWebSocketListener();
WebSocket webSocket = mClient.newWebSocket(request, listener);
mClient.dispatcher().executorService().shutdown();
}

完全なコードは次のとおりです。

MainActivity.java


import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

public class MainActivity extends AppCompatActivity {

private Button buttonSend;
private TextView textResult;
private OkHttpClient mClient;

private final class EchoWebSocketListener extends WebSocketListener {
private static final int CLOSE_STATUS = 1000;
@Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("What's up ?");
webSocket.send(ByteString.decodeHex("abcd"));
webSocket.close(CLOSE_STATUS, "Socket Closed !!");
}
@Override
public void onMessage(WebSocket webSocket, String message) {
print("Receive Message: " + message);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
print("Receive Bytes : " + bytes.hex());
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(CLOSE_STATUS, null);
print("Closing Socket : " + code + " / " + reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable throwable, Response response) {
print("Error : " + throwable.getMessage());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonSend = (Button) findViewById(R.id.buttonSend);
textResult = (TextView) findViewById(R.id.textResult);
mClient = new OkHttpClient();
buttonSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
start();
}
});
}
private void start() {
Request request = new Request.Builder().url("ws://echo.websocket.org").build();
EchoWebSocketListener listener = new EchoWebSocketListener();
WebSocket webSocket = mClient.newWebSocket(request, listener);
mClient.dispatcher().executorService().shutdown();
}
private void print(final String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
textResult.setText(textResult.getText().toString() + "\n" + message);
}
});
}
}

### 参照

以下はダウンロードリンクです。

番号リンク
1.ダウンロード コード
2.フォロー コード作成者

例 2: Okhttp を使用した Kotlin Android Websocket の例

これは別の Android WebSocket の例ですが、今回は Kotlin で書かれています。 依然としてネットワーク ライブラリとして OkHttp を使用しています。

ステップ 1: プロジェクトの作成

まず、空の「Android Studio」プロジェクトを作成します。

ステップ 2: 依存関係

2 つの OkHttp ライブラリをインストールします。

    implementation 'com.squareup.okhttp3:okhttp:3.12.6'
implementation 'com.squareup.okhttp3:mockwebserver:3.12.1'

これらを「app/build.gradle」に追加して同期します。

ステップ 3: レイアウトを設計する

以下に示すように、「MainActivity」のレイアウトにいくつかのボタンと編集テキストを追加します。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<Button
android:id="@+id/connectBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android : text = " Connect "
app:layout_constraintRight_toLeftOf="@+id/clientSendBtn"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/clientSendBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android : text = " Send from the client "
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/closeConnectionBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android : text = " Client is closed "
app:layout_constraintLeft_toRightOf="@+id/clientSendBtn"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/contentEt"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:background="@color/colorGray"
android:enabled="false"
android:gravity="top"
android:padding="5dp"
android:textColor="@color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/clientSendBtn"
app:layout_constraintVertical_weight="1" />

</androidx.constraintlayout.widget.ConstraintLayout>

ステップ 4: メッセージ リスナーを作成する

これは、接続のステータスに応じて、たとえば接続が成功したとき、閉じたとき、失敗したときなど、いくつかのコールバックが発生するインターフェイスになります。

MessageListener.kt

interface MessageListener {
fun onConnectSuccess () // successfully connected
fun onConnectFailed () // connection failed
fun onClose () // close
fun onMessage(text: String?)
}

ステップ 5: Websocket マネージャーを作成する

「WebSocketM」を作成するanager.kt` に次のインポートを追加することから始めます。

import  android.util.Log
import okhttp3.*
import okio.ByteString
import java.util.concurrent.TimeUnit

次のプライベート フィールドを含む WebSocketManager オブジェクト クラスを作成します。

object  WebSocketManager {
private val TAG = WebSocketManager::class.java.simpleName
private const val MAX_NUM = 5 // Maximum number of reconnections
private const val MILLIS = 5000 // Reconnection interval, milliseconds
private lateinit var client: OkHttpClient
private lateinit var request: Request
private lateinit var messageListener: MessageListener
private lateinit var mWebSocket: WebSocket
private var isConnect = false
private var connectNum = 0

次に、init 関数に URL と MessageListener を渡します。 ここで「OkHTTP」クライアントを初期化します。

    fun init(url: String, _messageListener: MessageListener) {
client = OkHttpClient.Builder()
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
request = Request.Builder().url(url).build()
messageListener = _messageListener
}

次に、接続する関数を作成します。

    fun connect() {
if (isConnect()) {
Log.i(TAG, "web socket connected")
return
}
client.newWebSocket(request, createListener())
}

再接続する関数も作成します。

    fun reconnect() {
if (connectNum <= MAX_NUM) {
try {
Thread.sleep(MILLIS.toLong())
connect()
connectNum++
} catch (e: InterruptedException) {
e.printStackTrace ()
}
} else {
Log.i(
TAG,
"reconnect over $MAX_NUM,please check url or network"
)
}
}

メッセージを送信する関数も用意します。

    fun sendMessage(text: String): Boolean {
return if (!isConnect()) false else mWebSocket.send(text)
}
fun sendMessage(byteString: ByteString): Boolean {
return if (!isConnect()) false else mWebSocket.send(byteString)
}

そして接続を閉じる関数:

    fun close() {
if (isConnect()) {
mWebSocket.cancel()
mWebSocket.close( 1001 , "The client actively closes the connection " )
}
}

完全なコードは次のとおりです。

WebSocketManager.kt

import  android.util.Log
import okhttp3.*
import okio.ByteString
import java.util.concurrent.TimeUnit

object WebSocketManager {
private val TAG = WebSocketManager::class.java.simpleName
private const val MAX_NUM = 5 // Maximum number of reconnections
private const val MILLIS = 5000 // Reconnection interval, milliseconds
private lateinit var client: OkHttpClient
private lateinit var request: Request
private lateinit var messageListener: MessageListener
private lateinit var mWebSocket: WebSocket
private var isConnect = false
private var connectNum = 0
fun init(url: String, _messageListener: MessageListener) {
client = OkHttpClient.Builder()
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
request = Request.Builder().url(url).build()
messageListener = _messageListener
}

/**
* connect
*/
fun connect() {
if (isConnect()) {
Log.i(TAG, "web socket connected")
return
}
client.newWebSocket(request, createListener())
}

/**
* Reconnection
*/
fun reconnect() {
if (connectNum <= MAX_NUM) {
try {
Thread.sleep(MILLIS.toLong())
connect()
connectNum++
} catch (e: InterruptedException) {
e.printStackTrace ()
}
} else {
Log.i(
TAG,
"reconnect over $MAX_NUM,please check url or network"
)
}
}

/**
* Whether to connect
*/
fun isConnect(): Boolean {
return isConnect
}

/**
* send messages
*
* @param text string
* @return boolean
*/
fun sendMessage(text: String): Boolean {
return if (!isConnect()) false else mWebSocket.send(text)
}

/**
* send messages
*
* @param byteString character set
* @return boolean
*/
fun sendMessage(byteString: ByteString): Boolean {
return if (!isConnect()) false else mWebSocket.send(byteString)
}

/**
* Close connection
*/
fun close() {
if (isConnect()) {
mWebSocket.cancel()
mWebSocket.close( 1001 , "The client actively closes the connection " )
}
}

private fun createListener(): WebSocketListener {
return object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response
) {
super.onOpen(webSocket, response)
Log.d(TAG, "open:$response")
mWebSocket = webSocket
isConnect = response.code() == 101
if (!isConnect) {
reconnect()
} else {
Log.i(TAG, "connect success.")
messageListener.onConnectSuccess()
}
}

override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
messageListener.onMessage(text)
}

override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
super.onMessage(webSocket, bytes)
messageListener.onMessage(bytes.base64())
}

override fun onClosing(
webSocket: WebSocket,
code: Int,
reason: String
) {
super.onClosing(webSocket, code, reason)
isConnect = false
messageListener.onClose()
}

override fun onClosed(
webSocket: WebSocket,
code: Int,
reason: String
) {
super.onClosed(webSocket, code, reason)
isConnect = false
messageListener.onClose()
}

override fun onFailure(
webSocket: WebSocket,
t: Throwable,
response: Response?
) {
super.onFailure(webSocket, t, response)
if (response != null) {
Log.i(
TAG,
"connect failed:" + response.message()
)
}
Log.i(
TAG,
"connect failed throwable:" + t.message
)
isConnect = false
messageListener.onConnectFailed()
reconnect()
}
}
}
}

ステップ 6: MainActivity コードを作成する

MainActivity の完全なコードは次のとおりです。

MainActivity.kt

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.thread

class MainActivity : AppCompatActivity(), MessageListener {
private val serverUrl = "ws://192.168.18.145:8086/socketServer/abc"
override fun onCreate(savedInstanceState: Bundle?) {
super .onCreate (savedInstanceState)
setContentView(R.layout.activity_main)
WebSocketManager.init(serverUrl, this)
connectBtn.setOnClickListener {
thread {
kotlin.run {
WebSocketManager.connect()
}
}
}
clientSendBtn.setOnClickListener {
if ( WebSocketManager .sendMessage( " Client send " )) {
addText( " Send from the client \n " )
}
}
closeConnectionBtn.setOnClickListener {
WebSocketManager.close()
}
}

override fun onConnectSuccess() {
addText( " Connected successfully \n " )
}

override fun onConnectFailed() {
addText( " Connection failed \n " )
}

override fun onClose() {
addText( " Closed successfully \n " )
}

override fun onMessage(text: String?) {
addText( " Receive message: $text \n " )
}

private fun addText(text: String?) {
runOnUiThread {
contentEt.text.append(text)
}
}

override fun onDestroy() {
super .onDestroy ()
WebSocketManager.close()
}
}

### 走る

コードをコピーするか、下のリンクからダウンロードし、ビルドして実行します。

### 参照

参考リンクは次のとおりです。

番号リンク
1.ダウンロード
2.フォロー コード作成者