Websocket

Websockets communication is established through an initial handshake.

That is, the client sends a GET request to the server in the following format

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

The server responds with a socket key which is computed with concatenating Sec-WebSocket-Key and 258EAFA5-E914-47DA-95CA-C5AB0DC85B11, taking the SHA1-hash of the result and return the base64 encoding of the hash.

The response will be in the following format containing the response key

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Only after the server respons with the accept key, client will be able to proceed with the communication, which are also transmitted in a encoded format called frames

Therefore, implementation of websockets can be quite heavy for certain embedded systems MCs like Arduino UNO

Implementation with OkHttp

1) We will handle websocket with external library OkHttp. Add the following line to Gradle.scripts corresponding to app module

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

2) Permission to use internet is needed

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

2) OkHttp uses its own background thread to send and receive messages. So the user need not have to worry about the work allocations. Reference

Send data from motion sensor onto a websocket

1) We use a demo socket server which echos the sent messages

2) Socket connection is opened when start is pressed. Similarly, connection is stopped on pressing stop / when the app is paused

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

public class MainActivity extends AppCompatActivity {

    private Button mStart;
    private Button mStop;
    private TextView mTextView;
    private OkHttpClient client;
    private WebSocket mWs;


    private final class EchoWebSocketListener extends WebSocketListener{

        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            super.onOpen(webSocket, response);
            webSocket.send("Hello!");
            webSocket.send("Connection established!");
            Log.i("Ws", "New ws created");
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            super.onMessage(webSocket, text);
            // on receiving message
            mTextView.setText(mTextView.getText().toString() + "\n" + text);

        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
            super.onClosing(webSocket, code, reason);
            mTextView.setText(mTextView.getText().toString() + "\n\n" + reason);
            Log.i("Ws", "ws closed");

        }
    }

    float mTilt;
    private SensorManager mSensorManager;
    private Sensor mSensor;
    private SensorEventListener mSensorEventListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            mTilt = event.values[0];
            // OkHttp executes send in the background thread
            mWs.send(String.valueOf(mTilt));
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };



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

        client = new OkHttpClient();
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mTextView = (TextView) findViewById(R.id.output);
        mTextView.setMovementMethod(new ScrollingMovementMethod()); // for scrolling

        mStart = (Button) findViewById(R.id.start_btn);
        mStart.setOnClickListener( new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                // create new connection
                Request request = new Request.Builder().url("ws://echo.websocket.org").build();
                EchoWebSocketListener listener = new EchoWebSocketListener();
                mWs = client.newWebSocket(request,listener);
                if(mSensor!=null)
                    mSensorManager.registerListener(mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL);

            }
        });

        mStop = (Button) findViewById(R.id.stop_btn);
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mSensor!=null)
                    mSensorManager.unregisterListener(mSensorEventListener);
                if(mWs != null){
                    mWs.close(1000,"Goodbye");
                }
            }
        });
    }

    @Override
    protected void onPause() {
        // stop connections when paused
        mStop.callOnClick();
        super.onPause();
    }
}