Android: How To Connect WebSocket using OkHttp, Flow and MVVM

Reza Ramadhan Irianto
3 min readJan 17, 2022

Websocket adalah salah satu cara untuk koneksi realtime antara Backend dengan frontend. Jika kalian mengenal API sebelumnya, konsep websocket ini sedikit berbeda dengan API. Pada dasarnya API menerapkan konsep request dan response, dimana ketika client meminta request maka server akan memberikan response yang diminta. Sedangkan websocket kedua pihak dapat mengirim data sesuka hati, lalu dengan melakukan listen terhadap data yang berubah.

Dalam projek kali ini kita akan menggunakan library dari square untuk mengambil data dari HTTP yaitu OkHttp. Kalian dapat melihat cara menginstall okhttp di https://github.com/square/okhttp. Selain Okhttp kita membutuhkan 2 library tambahan yaitu ViewModel Extension dan Coroutines. Jika kalian belum mengetahui tentang ViewModel dan Coroutines, lebih baik kalian coba research terlebih dahulu tentang kedua library itu. Kalian dapat melihat cara menginstallnya di https://developer.android.com/kotlin/ktx dan https://github.com/Kotlin/kotlinx.coroutines. Jadi total 3 library yang kita gunakan untuk melakukan akses ke WebSocket. Di projek ini juga kita akan menerapkan MVVM (Model View ViewModel).

implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

Oke, seperti yang kita lihat diatas kita membutuhkan 3 library itu, OkHttp untuk melakukan koneksi dengan WebSocket, Kotlin Coroutines untuk jembatan antara Data Source dengan ViewModel dan ViewModel ktx agar kita dapat mengakses ViewModelScope milik CoroutineScope.

Buat file dengan nama SocketManager.kt

class SocketManager (val coroutineScope: CoroutineScope): WebSocketListener() {

val output = Channel<String>()

override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)

coroutineScope.launch {
output.send(text)
}
}
}

Disini kita hanya implement method onMessage saja, jika kalian ingin handle error atau yang lainnya, kalian bisa implement method lain yang dimiliki interface WebSocketListener. Disini kita menggunakan Channel milik Coroutines untuk mengirim hasil dari websocket tersebut. Dalam SocketManager kita membutuhkan CoroutineScope untuk mengakses Channel yang dimiliki Coroutines.

Setelah itu kita buat SocketDataSource

class SocketDataSource(val coroutineScope: CoroutineScope) {
var mSocket: WebSocket? = null
val socketListener = SocketManager(coroutineScope)
fun connect(){
val client = OkHttpClient() y
val request: Request = Request.Builder().url("MASUKKAN _LINK_DALAM_PIE_SOCKET").build()
mSocket = client.newWebSocket(request, socketListener)
}

fun send(str: String){
mSocket?.send(str)
}

fun observeData(): Channel<String> {
return socketListener.output
}
}

Dalam SocketDataSource juga kita masih memerlukan CoroutineScope untuk kita passing ke SocketManager. Dalam SocketDataSource kita membutuhkan 3 function:

  1. connect(): untuk melakukan koneksi ke SocketDataSource.
  2. send(): untuk mengirim data ke WebSocket.
  3. observeData(): yang digunakan untuk listen data yang didapat dari variable output dari SocketManager.

Disini kita menggunakan Socket dari PieSocket, berikut link dari PieSocket yang bisa kalian akses secara mudah tanpa login https://www.piesocket.com/websocket-tester

Setelah itu kita buat file SocketRepository.kt

class SocketRepository(val coroutineScope: CoroutineScope) {
val dataSource = SocketDataSource(coroutineScope)
fun connect(){
dataSource.connect()
}

fun send(str: String){
dataSource.send(str)
}

fun observeData(): Flow<String> {
return dataSource.observeData().receiveAsFlow()
}
}

Pada dasarnya isi dari SocketRepository hanya return dari SocketDataSource. Yang berbeda adalah kita melakukan konversi dari Channel ke Flow agar dapat diobserve secara realtime di ViewModel. Terlihat di layer repository ini sangat simple, itu karena dalam aplikasi kita kali ini sangat simple dan terlihat seperti boilerplate, tapi jika aplikasi yang kalian buat sangat kompleks layer Repository ini akan sangat berguna nantinya.

Selanjutnya kita buat file SocketViewModel.kt

class SocketViewModel: ViewModel() {
var coins : MutableLiveData<String> = MutableLiveData()
val repo = SocketRepository(viewModelScope)


fun connect(){
repo.connect()
}

fun send(str: String){
repo.send(str)
}

fun observe(){
viewModelScope.launch {
repo.observeData().collect{
coins.value = it
}
}
}
}

Note: Disini saya menggunakan MutableLiveData yang di observe di activity, yang mana itu tidak disarankan karena akan menimbulkan MemoryLeak.

Disini kita melakukan konversi lagi dari Flow ke LiveData (Kalian bisa menggunakan yang lain seperti SharedFlow atau StateFlow) agar bisa di observe dalam Activity.

Dan terakhir di MainActivity.kt

class MainActivity : AppCompatActivity() {
private lateinit var viewModel: SocketViewModel

override fun onCreate(savedInstanceState: Bundle?) {
...

viewModel = ViewModelProvider(this).get(SocketViewModel::class.java)
viewModel.connect()
viewModel.send("asdfasdf")
viewModel.observe()
viewModel.coins.observe(this, {
println("TAG_OBSERVE: $it")
})

}

}

Disini kita memanggil function connect, send, dan observe. Dan akhirnya kita tinggal observe variable coins yang dimiliki SocketViewModel.

Dan akhirnya kita selesai melakukan implementasi WebSocket di Android, dalam contoh projek tersebut sangat simple, dan ada beberapa code yang saya tidak sarankan untuk gunakan seperti akses MutableLiveData dalam activity, mendeklarasi DataSource dalam Repository dan lain sebagainya. Sekian dan terima kasih.

https://github.com/rezaramadhanirianto/Simple-Websocket-Android

--

--