LoginSignup
4
4

More than 5 years have passed since last update.

ActionCableをAndroidからOkHttpで使ってみる

Last updated at Posted at 2016-11-09

動機

RailsのWebアプリケーションを作る際に、ActionCableでお手軽にWebSocketを行いたい。
けど、今後の拡張性を考えると、Android/iOSともWebSocketでつなげる必要がある。
とりあえず、Androidで出来ればiOSでも出来るだろう。

先に結論

とりあえず、JSONのフォーマットさえちゃんとしたら、Androidでも動くっぽい。
ただ、 disable_request_forgery_protectionしてるので、本番では問題あるかも?

クライアント側でもある程度実装が必要っぽいので、本番では使わないかなぁ。
(素のWebSocketでの実装・その他の実装も比較する必要はありますが。)

前提

サーバサイド

Ruby: 2.3.1
Rails: 5.0.0.beta1 (バージョン上げようとしたらいくつかエラーが出たので断念。)

https://github.com/JunichiIto/campfire を一部改変して利用しました。(heroku readyなのがありがたい)

クライアント

Android: 4.2.2
okhttp: 3.4.2

サーバサイドの準備

サンプルから改変

https://github.com/JunichiIto/campfire をcloneしてくる。

.ruby-versionGemfileに記載されているRubyのバージョンを、2.3.1に変更。

config/environments/production.rb でrequest originが https?で始まるものに限定していたので、その指定すら無くすようにしました。

-  config.action_cable.allowed_request_origins = [ /https?:\/\/.*/ ]
+  # config.action_cable.allowed_request_origins = [ /https?:\/\/.*/ ]
+  config.action_cable.disable_request_forgery_protection = true

(これって、http headerとかで見ているのでしょうか?本来であれば、何かしら制限しておく必要がある?)

herokuにデプロイ

READMEに書いてある通り、herokuにアップロードする。
heroku open するとURLが分かるので、後でAndroidの実装に組み込む。

クライアントの実装

ライブラリのインポート

compile 'com.squareup.okhttp3:okhttp:3.4.2'
compile 'com.squareup.okhttp3:okhttp-ws:3.4.2'

(ただ、 https://github.com/square/okhttp/pull/2852 を見ると、次のバージョンとかでいろいろ変わるかも?)

送受信の実装

今回は、ActivityのonCreateで接続するようにしました。
結果はログに出るだけです。

MainActivity.java
package hm.orz.chaos114.websocketsample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.ws.WebSocket;
import okhttp3.ws.WebSocketCall;
import okhttp3.ws.WebSocketListener;
import okio.Buffer;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

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

        Request request = new Request.Builder()
                .url("wss://INPUT_YOUR_DOMEIN.herokuapp.com/cable") // INPUT_YOUR_DOMEINは、heroku openしたときのドメインを指定してください。
                .build();

        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        WebSocketCall call = WebSocketCall.create(client, request);
        call.enqueue(new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                Log.d(TAG, "onOpen");

                try {
                    webSocket.sendMessage(RequestBody.create(WebSocket.TEXT, "{\"command\":\"subscribe\",\"identifier\":\"{\\\"channel\\\":\\\"RoomChannel\\\"}\"}"));
                    webSocket.sendMessage(RequestBody.create(WebSocket.TEXT, "{\"command\":\"message\",\"identifier\": \"{\\\"channel\\\":\\\"RoomChannel\\\"}\",\"data\":\"{\\\"message\\\":\\\"sample message!!!\\\",\\\"action\\\":\\\"speak\\\"}\"}"));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFailure(IOException e, Response response) {
                Log.d(TAG, "onFailure " + response, e);
            }

            @Override
            public void onMessage(ResponseBody message) throws IOException {
                Log.d(TAG, "onMessage " + message.string());
            }

            @Override
            public void onPong(Buffer payload) {
                Log.d(TAG, "onPong");
            }

            @Override
            public void onClose(int code, String reason) {
                Log.d(TAG, "onClose");
            }
        });
    }
}

起動すると、RoomChannelに接続し、sample message!!!というテキストを送信します。
heroku openしたブラウザを確認すると、メッセージが追加されていくはずです。

仕組みの考察メモ

  • ApplicationCable::Channel を継承したクラス名が、Channel名となる。
  • Channel名を指定して、subscribeコマンドを発行すると、受信可能状態になる。
  • ApplicationCable::Channel を継承したクラスのメソッドが、Action名になる。
  • Action名を指定して、messageコマンドを発行すると、メッセージを送信できる。
  • 部屋を分ける場合は、 https://github.com/rails/rails/tree/master/actioncable#passing-parameters-to-channel このへんを参考にしたら出来るかも?

参考

いろいろ実装したあとで、 https://github.com/hosopy/actioncable-client-java を見つけました。
コレ使えばよかったのかも。。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4