Koneksi ke Websocket di Android menggunakan Scarlet


 

Halo selamat siang bagi yang merayakannya. Menyambut bulan suci Ramadan, mari kita isi dengan menulis sesuatu yang berfaedah. Kali ini saya akan menulis tentang koneksi ponsel Andriod ke websocket menggunakan librari Scarlet. Disclaimer: Ini cuma catatan pribadi saja supaya tidak lupa.

Sebelumnya, apa itu websocket? Menurut Wikipedia, websocket adalah salah satu protokol komunikasi yang menyediakan fitur chanel komunikasi full-duplex dalam koneksi TCP. Artinya apa? Entahlah, untuk detailnya silakan saja baca di sini.

Salah satu kegunaan websocket adalah dalam aplikasi chat yang mengharuskan peramban/ponsel kita selalu realtime menerima data. Kan tidak elok jika hari ini kita kirim pesan ke orang lain, baru diterima esok hari. Kegunaan lain dari protokol ini adalah buat aplikasi yang memang mengharuskan data dikirim secara realtime, misal skor judi bola terbaru atau harga komoditas saat ini.

Memang sih kita bisa menggunakan librari macam retrofit dan mengakses url REST APi biasa agar mendapatkan data yang berulang, menggunakan fitur interval dan repeat() seperti di bawah, tapi katanya langkah seperti ini tidak disarankan.

Observable.interval(0,10, TimeUnit.SECONDS) 
    .flatMap { api.getUpdateSkor() } 
    .repeat() 
    .subscribe({ 
        Timber.d("output: ${it.source()}") 
    }, { e -> e.printStackTrace() })
<p>&nbsp;</p>

Salah satu cara yang katanya disarankan adalah lewat protokol websocket. Alih-alih berulangkali meminta server kasih data via REST API, kita melakukan subscribe dan menunggu server mengirimkan datanya secara terus menerus. Nah salah satu librari yang bisa digunakan adalah Scarlet buatan Tinder.

Dari deskripsi di reponya, Scarlet diklaim sebagai client websocket untuk Android yang terinspirasi dari Retrofit. Jika Retrofit banyak digunakan untuk kepentingan akses REST API, Scarlet untuk protokol websocket.

Saat tulisan ini dibuat, Scarlet baru mencapai versi 0.2.4 dengan dokumentasi yang masih menggunakan versi sebelumnya, 0.1.7. Ada beberapa perbedaan antara versi terbaru dengan sebelumnya, salah satunya tidak adanya fitur Scarlet.Builder(). Jadi bagi anda yang mau coba librari ini, pastikan dulu menggunakan versi yang mana, agar tidak bingung. Saya sendiri pakai versi 0.2.4.

Untuk mencoba websocket ini, kita contek mentah-mentah saja sampel yang dari repo Githubnya, dengan beberapa penyesuaian. Kita akan buat aplikasi yang menampilkan data harga Bitcoin ter-update yang bersumber dari Gdax.com alias coinbase.

Karena cuma sampel, mari kita buat aplikasinya sesimpel mungkin sehingga mari kurangi librari-librari lain yang digunakan, terutama Dagger dan kawan-kawannya :D. Juga tanpa arsitektur macam-macam seperti MVVM atau MVP. Spageti kode is debes!!!1!

Pertama, kita impor dulu librari-librari yang akan digunakan. Di sini saya menggunakan librari Scarlet (versi terbaru) beserta perintilannya, Gson, OkHttp, dan RxJava.

implementation 'com.github.tinder.scarlet:scarlet:0.2.4'
implementation "com.github.tinder.scarlet:scarlet-stream-adapter-rxjava2:0.2.4"
implementation "com.github.tinder.scarlet:scarlet-message-adapter-gson:0.2.4"
implementation "com.github.tinder.scarlet:scarlet-protocol-websocket-okhttp:0.2.4"
implementation "io.reactivex.rxjava2:rxkotlin:2.3.0"
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
implementation "com.squareup.okhttp3:logging-interceptor:3.14.1"
implementation "com.squareup.okhttp3:okhttp:3.14.1"
implementation "com.google.code.gson:gson:2.8.5"
<p>&nbsp;</p>

Kalau sudah disinkronisasi, mari buat berkas interface untuk akses servisnya. Kelas interface SocketServiceApi yang dibuat mirip dengan kelas interface pada Retrofit kebanyakan.

interface SocketServiceApi { 
    @Send 
    fun sendSubscribe(subscribe: Subscribe) 
    @Receive 
    fun observeTicker(): Flowable<Ticker> 
    @Receive 
    fun observeOnConnectionOpenedEvent(): Flowable<WebSocketEvent>
}

 

Berikut kelas data untuk Subscribe dan Ticker. Sampai sini, sekilas tidak ada beda dengan Retrofit kan ya.

data class Subscribe(
     val type: String = "subscribe",
     @SerializedName("product_ids")
     val productIds: List,
     val channels: List
 )

data class Ticker(
     val time: String,
     val price: String
 )

Sekarang kita aplikasikan di MainActivity. Kita buat objek Subscribe, koin mana yang akan kita subscribe ke Gdax.

private val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = Subscribe(
         productIds = listOf("BTC-USD"),
         channels = listOf("ticker")
     )

Lalu kita buat komponen-komponen yang akan digunakan oleh Scarlet, seperti klien okHttp dan Gson untuk menerjemahkan Json ke bentuk objek java eh kotlin.

private val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
    .build()

private val gson = GsonBuilder()
    .serializeNulls()
    .create()

Di Scarlet yang terbaru, alih-alih menggunakan Scarlet.Builder() sesuai contoh di readme-nya, inisialisasi servis socket kini harus menyertakan objek protocol dan configuration. Mari kita buat.

private val protocol = OkHttpWebSocket(
    okHttpClient,
    OkHttpWebSocket.SimpleRequestFactory(
        { Request.Builder().url("wss://ws-feed.gdax.com").build() },
        { ShutdownReason.GRACEFUL }
    )
)

private val configuration = Scarlet.Configuration(
    messageAdapterFactories = listOf(GsonMessageAdapter.Factory(gson)),
    streamAdapterFactories = listOf(RxJava2StreamAdapterFactory())
)

private val service = Scarlet(protocol, configuration).create<SocketServiceApi&gt;()

Di berkas layout, masukkan widget tombol ke dalam layout yang sudah disertakan:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" android:id="@+id/txt"/&gt;
<Button
        android:text="@android:string/ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
        app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="8dp"
        android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/txt"
        android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent"/&gt;

Karena menggunakan Kotlin, kita tidak usah lagi memanggil findViewById, cukup panggil nama id-nya langsung di MainActivity. Di onCreate(), tambahkan kode berikut:

    button.setOnClickListener {
        doSubscribeSocket()
    }

Implementasi interface SocketServiceApi dilakukan di fungsi doSubscribeSocket. Mari kita implementasikan.

@SuppressLint("CheckResult")
private fun doSubscribeSocket() {

    service.observeOnConnectionOpenedEvent()
        .filter{ it is WebSocketEvent.OnConnectionOpened }
        .subscribe({
            service.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE)
        }, {e-&gt;e.printStackTrace()})

    service.observeTicker()
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe ({ ticker -&gt;
            val text = "BTC: ${ticker.price} at ${ticker.time}"
            Log.d("WEBSOCKET",text)
            txt.text = text
        }, {e -&gt; e.printStackTrace()})
}

Pada kode di atas, kita melakukan subscribe koin BTC sesuai yang didefinisikan oleh BITCOIN_TICKER_SUBSCRIBE_MESSAGE, dan hasilnya akan ditampilkan di logcat dan textView secara realtime. Setelah itu, mari kita build dan jalankan di ponsel atau emulator.

Berhasil? harusnya tidak ya. Karena kita belum memberikan izin agar aplikasi dapat mengakses jaringan internet. Silakan buka berkas AndroidManifest.xml dan masukkan izin untuk internet.

    <uses-permission android:name="android.permission.INTERNET"/&gt;

Seharusnya sekarang aplikasinya sudah bisa mengakses internet. Coba klik tombol OK dan lihat di logcat, apakah ada permintaan ke websocket atau tidak. Kalau berhasil, harusnya muncul logcat seperti ini:

D/OkHttp: <-- 101 Switching Protocols https://ws-feed.gdax.com/ (1514ms)
Date: Mon, 20 May 2019 05:46:02 GMT
Connection: upgrade
Set-Cookie: __cfduid=dc262c3dff3a091b7fad739f054ce52921558331161; expires=Tue, 19-May-20 05:46:01 GMT; path=/; domain=.gdax.com; HttpOnly
Upgrade: websocket
Sec-WebSocket-Accept: gphFpV/mjYVdZPblYInF3bTw5uA=
Sec-WebSocket-Version: 13
WebSocket-Server: uWebSockets
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
D/OkHttp: Server: cloudflare
CF-RAY: 4d9c01fcdb84c34f-SIN
<-- END HTTP

Setelah itu tunggulah hingga ada jawaban dari server Gdax. Di saya, ada delay lumayan lama (sekitar 5-10 detik) sebelum data harga Bitcoin muncul di logcat.

D/WEBSOCKET: BTC: 7959.83000000 at null
D/WEBSOCKET: BTC: null at null
D/WEBSOCKET: BTC: 7968.14000000 at 2019-05-20T05:47:05.895000Z
D/WEBSOCKET: BTC: 7968.82000000 at 2019-05-20T05:47:05.895000Z
D/WEBSOCKET: BTC: 7968.03000000 at 2019-05-20T05:47:11.736000Z
D/WEBSOCKET: BTC: 7968.27000000 at 2019-05-20T05:47:16.116000Z
D/WEBSOCKET: BTC: 7967.19000000 at 2019-05-20T05:47:16.116000Z
D/WEBSOCKET: BTC: 7967.90000000 at 2019-05-20T05:47:18.635000Z
D/WEBSOCKET: BTC: 7967.89000000 at 2019-05-20T05:47:18.725000Z
D/WEBSOCKET: BTC: 7971.84000000 at 2019-05-20T05:47:20.151000Z

Begini tampilan di aplikasinya.

Harga yang muncul akan diupdate secara otomatis

Kode lengkapnya bisa dilihat di sini.

Ya sudah, sekian saja tulisannya. Bagaimana, mudah sekali, kan, pemirsa?


One response to “Koneksi ke Websocket di Android menggunakan Scarlet”

Ada komentar?

This site uses Akismet to reduce spam. Learn how your comment data is processed.