Androidでsocket.io

2014-01-11T00:00:00+00:00 Android Java JavaScript Node.js

socket.io-java-clientを使えば出来るらしいのでちょっと要件検証目的でやってみた。ちなみにsocket.ioサーバーはnode.jsなやつを使う

サーバー側

npm install -g socket.io

-gオプションつける必要あるかどうかはおいといて

var io = require("socket.io").listen(8080);
io.sockets.on("connection", function(socket) {
  socket.on("message", function(data) {
    // 送った自身以外のクライアントへsend
    socket.broadcast.send(data);
  });
});

的なsocket.ioサーバーを作って起動しておく

クライアント側 (ブラウザ)

ブラウザで使うsocket.ioのライブラリ(http://socket.io/#how-to-useで書かれてるやつ)は、npmでインストールしたsocket.ioパッケージ内のnode_modules/socket.io-client/dist内に入ってるのでコピペでしておく

<html>
  <head>
    <script type="text/javascript" src="socket.io.js"></script>
  </head>
  <body>
    <script type="text/javascript">
(function(undefined) {
  var socket = io.connect("http://[socket.io.server.ip]:8080");
  socket.on("connect", function() {
    socket.send("browser(send)");

    socket.on("message", function(data) {
      console.log(data);
    });
  });
})();
    </script>
  </body>
</html>

socket.sendとsocket.emitってのがあるのけどsocket.sendだけ使う。socket.sendだとmessageというイベントキーによりデータをキャッチ出来るので(ry

クライアント側 (Android)

上記にも書いてるsocket.io-java-clientをダウンロードしてくる。ビルドの仕方は書いてるので省略する。socketio.jarをAndroidプロジェクトのlibsディレクトリに突っ込んでおけば良い

でブラウザ側なクライアントから受信したデータをListViewで出すだけっていう単純な事をする

package sample.test.socketio;

import java.net.MalformedURLException;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import io.socket.IOAcknowledge;
import io.socket.IOCallback;
import io.socket.SocketIO;
import io.socket.SocketIOException;
import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getName();

    Handler mHandler;
    ArrayAdapter<string> mAdapter;
    SocketThread mThread;
    SocketIO mSocket;

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

        mHandler = new Handler();
        mAdapter = new ArrayAdapter<string>(this, android.R.layout.simple_list_item_1);

        ((ListView)findViewById(R.id.listView)).setAdapter(mAdapter);
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.v(TAG, "onStart");

        if (mThread == null) {
            mThread = new SocketThread();
            mThread.start();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.v(TAG, "onStop");

        if (mThread != null) {
            mThread.cancel();
            mThread = null;
        }
    }

    private class SocketThread extends Thread {

        @Override
        public void run() {
            try {
                if (mSocket == null) {
                    mSocket = new SocketIO("http://[socket.io.server.ip]:8080");
                    mSocket.connect(new IOCallback() {

                        @Override
                        public void onConnect() {
                            Log.v(TAG, "onConnect");
                            // connectしたらAndroidからsend
                            mSocket.send("android");
                        }

                        @Override
                        public void onDisconnect() {
                            Log.v(TAG, "onDisconnect");
                        }

                        @Override
                        public void onError(SocketIOException e) {
                            Log.v(TAG, "onError: " + e.getMessage());
                        }

                        @Override
                        public void on(String eventName, IOAcknowledge ack, Object... args) {
                            Log.v(TAG, "on");

                            for (Object arg : args) {
                                if (!(arg instanceof JSONObject))
                                    continue;

                                JSONObject json = (JSONObject)arg;
                                onMessage(json, null);
                            }
                        }

                        @Override
                        public void onMessage(String data, IOAcknowledge ack) {
                            Log.v(TAG, "onMessage");
                            update(data);
                        }

                        @Override
                        public void onMessage(JSONObject data, IOAcknowledge ack) {
                            Log.v(TAG, "onMessage(JSON)");

                            if (!data.has("msg"))
                                return;

                            try {
                                String msg = (String)data.get("msg");
                                update(msg);
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        private void update(final String data) {
                            mHandler.post(new Thread() {
                                @Override
                                public void run() {
                                    mAdapter.add(data);
                                    mAdapter.notifyDataSetChanged();
                                }
                            });
                        }
                    });
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }

        public void cancel() {
            if (mSocket != null) {
                mSocket.disconnect();
                mSocket = null;
            }
        }
    }
}

んまぁエラー処理とか適当なのでもうちょい厳密的にチェックしなきゃならんのですが。要はIOCallbackで適切なメソッドが呼び出されるってだけなんですが

  • emitならonメソッドが作用
  • sendならonMessage(以下参考)

っていうようなメソッドが発生する。んまぁonConnectとかそういう系はおいといて、onMessageのメソッドがオーバーロードにより複数存在するのには、socket.ioサーバーにおいて

socket.send("...")
socket.json.send({})

等によって処理されるメソッドが異なるっていう点は注意しなきゃならんかと

んまぁてな感じでやればブラウザクライアントから送ったデータがAndroidにも表示されて、Android側から送ったデータがHTML側にもブロードキャストされてるはず

終わり

参考: http://dev.classmethod.jp/smartphone/android-socket-io

nginxでwebsocket Spring WebMVCのformとFormatterの件