Create Websocket Android Application Using Ktor
Tags: kotlin, programming, tutorial, backend
Published: 2023-05-02 19:49:17

In this article i will show you the simple way to create webscocket android app (client side) and create websocket server side with Kotlin Ktor.


![project review](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mcaony279uu2us7jcj4c.jpg)

**First, Let's Create Server Side (websocket server)**
Open Intellij Ide or any ide you prefer then create a new project and select Ktor from left side then click next.


![Image 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/167mca2n0iws7u9uqve2.png)

now you will see project plugins page, Search for 'Routing' then add it & search for 'WebSockets' and add it then click Create


![Image 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l98clqrhzw8ox5dzlxxd.png)

after project builds you must have these files in your project:


![Image 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jfydp740f0g3z37pxysc.png)

Now open **build.gradle.kts** and add these dependencies

```bash
dependencies {
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
    implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version")
    implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
    implementation("ch.qos.logback:logback-classic:$logback_version")
    testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
}
```


![build.gradle.kts file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o63ds0hisyvofswdk81.png)

Now we want to create `application.conf` inside resources folder and add inside it these lines.

> Ktor uses this file to determine the port on which it should run, and it also defines the entry point of our application.


```kotlin
ktor {
    deployment {
        port = 8080
        port = ${?PORT}
    }
    application {
        modules = [ com.example.ApplicationKt.module ]
    }
}
```

now we have set the application entry point at com.example.ApplicationKt.module so let's go to Application.kt file, and this is our entry point.

> you don't have to create `application.conf` file you can use fun main() to put your port & host (ip) instead of it.


![Image 6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ldvd8ont3bb33u8xqjg.png)


then, go to Sockets.kt file and put this code in it


```kotlin
routing {
    webSocket("/chat") {
        send("You are connected!")
        for(frame in incoming) {
            frame as? Frame.Text ?: continue
            val receivedText = frame.readText()
            send("You said: $receivedText")
        }
    }
}
```


![final code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z1mncbscaadmvytjwnw.png)


Now Our server is ready, Let's run it by opening application.kt file and press on the green play button and you must see this at the terminal



![start server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rveyltaeurdmkjxgtpb9.png)

to check if our server is running properly or not, just open **Postman** & open new file & choose websocket request then put your server **ws://url:port/chat** in that scheme: **ws://127.0.0.1:8080/chat** and click connect


![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xyg3cbzak9h5cxejrv91.png)


now if you send any message you will see it at realtime so our server is running correctly that's awesome.
But this is so simple and not a chat, Let's create more advanced websocket chat
first create new file in your main project package called **Connections.kt** then add this code in it


```kotlin
package com.example

import io.ktor.websocket.*
import java.util.concurrent.atomic.*

class Connection(val session: DefaultWebSocketSession) {
    companion object {
        val lastId = AtomicInteger(0)
    }
    val name = "user${lastId.getAndIncrement()}"
}
```


![Image 8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2zcn4j4k8lyrqukue6y.png)


Go back to Sockets.kt and modify routing by adding these new codes


```kotlin
routing {
    val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())
    webSocket("/chat") {
        println("Adding user!")
        val thisConnection = Connection(this)
        connections += thisConnection
        try {
            send("There are ${connections.count()} users here.")
            for (frame in incoming) {
                frame as? Frame.Text ?: continue
                val receivedText = frame.readText()
                val textWithUsername = "[${thisConnection.name}]: $receivedText"
                connections.forEach {
                    it.session.send(textWithUsername)
                    println(textWithUsername)
                }
            }
        } catch (e: Exception) {
            println(e.localizedMessage)
        } finally {
            println("Removing $thisConnection!")
            connections -= thisConnection
        }
    }
}
```


re-run the server and try sending messages with **postman** again to see the results.


![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jpvteb1cyf32172apfgf.png)


now you see that the chat is little advanced and every user joins the chat takes unique username

> Now Our Server Is Ready To Be Linked With Our Android App :)


---

**Okay, Let's Create Client Side (Android Application)**
Open Android studio or any Ide you have, then create a new project & select Kotlin as a primary language.


![create project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xx77gkvqkmebrzagflw.png)

Then, open **build.gradle** app level file and put this implementation at dependencies.

```bash
dependencies {
    implementation 'org.java-websocket:Java-WebSocket:1.4.0'
}
```


> we will use View Binding in this project so don't forget to activate it at build.gradle file.


After gradle sync complete, open **AndroidManifest.xml** and put the internet permission.

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


then we will store server url at **strings.xml** go to '**res > values > strings.xml**' and add this line & modify your server url as you want

```xml
<string name="server_url" translatable="false">ws://127.0.0.1:8080</string>
```


Now we need to create chat ui so open **activity_main.xml** file and add this codes to add **ListView**, **EditText** and **Button**


```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/linear2"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:padding="4dp">

        <ListView
            android:id="@+id/listview1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:choiceMode="none"
            android:padding="8dp"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/linear1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="2dp">

        <EditText
            android:id="@+id/edit_msg"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="Enter Message"
            android:importantForAutofill="no"
            android:padding="8dp"
            android:textColor="#000000"
            android:textColorHint="#607D8B"
            android:textSize="12sp"
            tools:ignore="TextFields" />

        <Button
            android:id="@+id/btnSend"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="Send"
            android:textColor="#FFFFFF"
            android:textSize="16sp" />
    </LinearLayout>

</LinearLayout>
```


after that Create new kotlin class called **ChatWebSocketClient.kt** and add these line of code

> this class handle every action that happen like when connection open, close, error and when receive a message from server.


```kotlin
package com.encept.websocket_client

import org.java_websocket.client.WebSocketClient
import org.java_websocket.handshake.ServerHandshake
import java.net.URI

// initialize websocket client
class ChatWebSocketClient(serverUri: URI, private val messageListener: (String) -> Unit) : WebSocketClient(serverUri) {

    override fun onOpen(handshakedata: ServerHandshake?) {
        // When WebSocket connection opened
    }

    override fun onClose(code: Int, reason: String?, remote: Boolean) {
        // When WebSocket connection closed
    }

    override fun onMessage(message: String?) {
        // When Receive a message we handle it at MainActivity
        messageListener.invoke(message ?: "")
    }

    override fun onError(ex: Exception?) {
        // When An error occurred
    }

    fun sendMessage(message: String) {
        send(message)
    }
}
```

Now open **MainActivity.kt** and put these variables after class declaration


```kotlin
private lateinit var binding: ActivityMainBinding
private lateinit var webSocketClient: ChatWebSocketClient
private var AL: ArrayList<HashMap<String, Any>> = ArrayList()
```

Then, Add this **onCreate function** with these codes that initialize


```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    //declare the ui widgets using binding
    val btnSend = binding.btnSend
    val editMsg = binding.editMsg
    val listview1 = binding.listview1

    // load server url from strings.xml
    val serverUri = URI(getString(R.string.server_url))

    webSocketClient = ChatWebSocketClient(serverUri) { message ->
        // display incoming message in ListView
        runOnUiThread {
            run {
                val _item = HashMap<String, Any>()
                _item["message"] = message
                AL.add(_item)
            }
            listview1.adapter = ListviewAdapter(AL)
        }
    }
    // connect to websocket server
    webSocketClient.connect()

    btnSend.setOnClickListener {
        try {
            // send message to websocket server
            webSocketClient.sendMessage(editMsg.text.toString())
            editMsg.setText("")
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show()
        }
    }
}
```

> * in case of you're using IDE that does not support auto import just add these imports to your **MainActivity.kt**

```kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.encept.websocketchat.databinding.ActivityMainBinding
import java.net.URI
import android.widget.*
import java.util.*
```

Now we need to close Websocket connection when activity destroy.

```kotlin
override fun onDestroy() {
    super.onDestroy()
    // close websocket connection
    webSocketClient.close()
}
```


now we need to create listview custom view, so create new xml file called **custom.xml** and add this code we will just add TextView to display any message.


```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:orientation="horizontal"
        tools:ignore="UselessParent">

        <TextView
            android:id="@+id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text"
            android:textSize="16sp"
            android:textStyle="bold"
            android:textColor="#000000"/>
    </LinearLayout>

</LinearLayout>
```

After onCreate fun Add The **ListView Adapter**


```kotlin
class ListviewAdapter(private val _data: ArrayList<HashMap<String, Any>>) : BaseAdapter() {
    override fun getCount(): Int {
        return _data.size
    }

    override fun getItem(_index: Int): HashMap<String, Any> {
        return _data[_index]
    }

    override fun getItemId(_index: Int): Long {
        return _index.toLong()
    }

    override fun getView(_position: Int, _v: View?, _container: ViewGroup?): View? {
        val _inflater = LayoutInflater.from(_container?.context)
        var _view = _v
        if (_view == null) {
            _view = _inflater.inflate(R.layout.cul, _container, false)
        }

        val text2 = _view?.findViewById<TextView>(R.id.text2)

        if (text2 != null) {
            text2.text = _data[_position]["message"].toString()
        }

        return _view
    }

}
```

_That's all for today i hope this article helped you :)_

Sources and References:
[Ktor docs](https://ktor.io/docs/welcome.html)
[websocket client](https://github.com/kimoandroid/Android-WebSocketClient)
[websocket server](https://github.com/kimoandroid/Ktor-WebSocketServer)