Building a chat app for Android using Socketcluster in node.js

Sachin Shinde
4 min readDec 25, 2016

--

( get full code at https://github.com/sacOO7/socketcluster-android-demo )

A year ago, I was searching for available frameworks that can be used for building chat based applications. There were lots of available options like Pubnub, Pusher, Firebase, socket.io and socketcluster.

The problem with cloud based services like Pubnub, Pusher and Firebase is limitation on writing custom logic on the backend side and ultimately pricing. The socket.io seem to have larger developer community, quite good in terms of setting up environment and flexible API architecture. Still, I needed something more than this; a framework that is easy to scale and can handle millions of concurrent connections. Finally , I found a socketcluster framework as best solution which is powered by fastest available websocket engine uwebsockets.

chat demo

Introduction

To start developing chat app , clone socketcluster-android-demo for reference

This app provides following features :

  • Publishing message to all users subscribed to the given group
  • Notification when user starts typing
  • Notification when a user joins or leaves the group
  • No server code required; just start the server, client API logic will handle everything for you

Installing the dependencies

To install , add following dependency to build.gradle

// app/build.gradledependencies {
...
compile ('io.github.sac:SocketclusterClientJava:1.7.2'){
exclude group :'org.json', module: 'json'
}
}

Also need to add internet permission to AndroidManifest.xml

<!-- app/AndroidManifest.xml -->
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Start writing the code

For ease of reading code, I have embedded everything in a single activity and used Pub-Sub API to send publish and subscribe events to the server.

Creating an instance of socket inside Activity

Declare Socket as a member variable. Create a new instance of socket by passing the server URL inside the onCreate method.

import io.github.sac.Socket;
import io.github.sac.Ack;
import io.github.sac.BasicListener;
import io.github.sac.Emitter;
import io.github.sac.EventThread;
import io.github.sac.ReconnectStrategy;
public class MainActivity extends AppCompatActivity {String url="ws://192.168.0.4:8000/socketcluster/";
Socket socket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
socket = new Socket(url);
}

Setting basic callbacks and reconnect-strategy on the socket

Register callbacks for connected, disconnected, connecterror and authentication events using BasicListener . By default reconnection to the server is disabled; to enable it, pass instance of class ReconnectStrategy with number of attempts and delay.

@Override    
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
socket = new Socket(url);
socket.setListener(new BasicListener() {
public void onConnected(....) {
Log.i("Success ","Connected to endpoint");
}
public void onDisconnected(....){
Log.i("Success ","Disconnected from end-point");
}
public void onConnectError(....){
Log.i("Success ","Got connect error "+ exception);
}
public void onSetAuthToken(String token, Socket socket) {
socket.setAuthToken(token);
}
public void onAuthentication(Socket socket,Boolean status){
if (status) {
Log.i("Success ","socket is authenticated");
}else {
Log.i("Success ","Authentication is required
(optional)");
}
}
});//This will set max attempts to 10 and delay of 3 seconds
socket.setReconnection(new
ReconnectStrategy().setMaxAttempts(10).setDelay(3000));
socket.connectAsync();

Subscribe to channel

Subscribe to specific channel for emitting and listening to events. Let’s subscribe to a channel named ‘MyClassroom’ as soon as the socket is connected to the server.

public void onConnected(....) {
Log.i("Success ","Connected to endpoint");
socket.createChannel("MyClassroom").subscribe();
}

Publishing events

It’s quite easy to publish istyping, user joined, user left and message event on subscribed channel . For ease of sending and parsing, create instance of JSONObject and pass it as a data over the channel .

//For sending user joined eventJSONObject object=new JSONObject();
object.put("isjoined",true);
object.put("data","Harry joined the group");
socket.getChannelByName("MyClassroom").publish(object);
//or
//socket.publish("MyClassroom",object);
//For sending user left eventJSONObject object=new JSONObject();
object.put("isleft",true);
object.put("data","Harry left the group");
socket.getChannelByName("MyClassroom").publish(object);
//For sending istyping eventJSONObject object=new JSONObject();
object.put("istyping",true);
object.put("data","Harry is typing");
socket.getChannelByName("MyClassroom").publish(object);
//For sending message as eventJSONObject object=new JSONObject();
object.put("ismessage",true);
object.put("data",message); //message is string
socket.getChannelByName("MyClassroom").publish(object);

Listening to events

Set callback for listening to ‘MyClassroom’ channel using onSubscribe method. It is also important to parse data after receiving . To update data on user interface, execute runnable under ui thread.

socket.onSubscribe("MyClassroom",new Emitter.Listener() {    @Override            
public void call(String name, final Object data) {
try {
JSONObject object= (JSONObject) data;
//Parse data in accordance with flag set
...
...
String data= object.getString("data"); //To set data to textview , method needs to be executed
//on UI thread
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(data);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
});

Above code demonstrates how to use API in order to build a chat app . You can have your own custom UI layouts or you can just clone the repository, run it, make changes and repeat .

Getting internal socket state

Socket can exist in a number of states during its whole lifetime. States can be CLOSED, CLOSING, CONNECTING, CREATED, OPEN etc.

For getting internal state

WebSocketState state=socket.getCurrentState();
Log.i ("Success","Current state is "+state.toString());

To check if socket is connected

if (socket.isconnected()) {
Log.i ("Success","Socket is connected");
}

To disconnect from end-point

socket.disconnect();`

You can find detailed documentation for using client library here.

--

--