A small Kotlin client for phoenix channel

This is not the first one and might not be the last one, but I started using Kotlin for android development and I needed to communicate with Phoenix Channels. So I wrote a client library.

To help follow the JavaScript reference implementation, my kotlin implementation try to copy the JS as close as possible in term of code style and behavior.

At present it should be considered alpha sofware. I use it in my projects. But I thought it might be of interest for the community.

The repository is on Github https://github.com/kuon/java-phoenix-channel

9 Likes

Great work. I have managed to incorporate it inside my otherwise Java written Android application. I am calling the socket connection from inside a Service. There was an issue of joining channels multiple times and issues of failure to reconnect after internet connection loss. I have resolved those issues by this:

PhxSocket.java


public class PhxSocket extends Service {

    GoSocket goSocket;
    Socket socket;
    Socket.Options socketOptions;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void init_goSocket() {

        goSocket = null;

        try {
            socket.disconnect(null, null, null);
        } catch (Exception ignored) {
        }

        goSocket = new GoSocket();
        socketOptions = new Socket.Options();
        socketOptions.setHeartbeatIntervalMs(5_000);
        HashMap<String, Object> hashMap = new HashMap<String, Object>();
        hashMap.put("uuid", MainActivity._uuid());
        socketOptions.setParams(hashMap);
        socket = new Socket("https://my.app.com/socket", socketOptions);
        goSocket.initialize(MainActivity._uuid(), socket);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (socket == null || socket.isConnected() == false) {
            init_goSocket();
        }

        return Service.START_NOT_STICKY;
    }
}

GoSocket.kt

class GoSocket {

    fun initialize(uuid: String, sd: Socket) {

        sd.connect()

        val clientChannel = sd.channel("client:$uuid")

        clientChannel
                .join()
                .receive("ok") { msg ->
                
                }
                .receive("error") { msg ->
                
                }
                .receive("timeout") { msg ->
               
                }

        clientChannel.on("message:vc_notify") { msg ->
            println("Got a message: " + msg.response.toString())
            Intent().also { intent ->
                intent.action = "main-activity-broadcaster"
                intent.putExtra("main_activity_message", "vc_notify")
                LocalBroadcastManager.getInstance(MyApplication.getAppContext()).sendBroadcast(intent)
            }

        }

        clientChannel.on("message:vc") { msg ->
            println("Got a message: " + msg.response.toString())
            Intent().also { intent ->
                intent.action = "main-activity-broadcaster"
                intent.putExtra("main_activity_json", msg.response.toString())
                LocalBroadcastManager.getInstance(MyApplication.getAppContext()).sendBroadcast(intent)
            }

        }

        val connectedChannel = sd.channel("clients:connected")

        connectedChannel
                .join()
                .receive("ok") { msg ->
               
                }
                .receive("error") { msg ->
             
                }
                .receive("timeout") { msg ->
                   
                }
    }
}

I am not sure if there is a better way to do it. If so, kindly tell me.

I ported the JS code logic to kotlin, but the underlying websocket implementation has a bit different behavior compared to the browser web socket (in how lifecycle events are emited).

What I ended doing is when there is an error (not a regular error response from the server), I throw away both the channel and the socket (I call leave on channel and disconnect on socket and create new ones).

I didn’t have time to pinpoint the issue and patch my library.

As for your code, I see nothing wrong with it.

1 Like

I have omitted some part above. Actually, I have wrapped the following within a fixed scheduler task running every 15 seconds:

        if (socket == null || socket.isConnected() == false) {
            init_goSocket();
        }

Thanks for the great work.

1 Like