經過一晚的休息,老姐找到了癥結。
https://ktor.io/docs/clients-websockets.html 的範例沒有問題,有問題的是 Library。
老姐抱怨:「還以為 android 該用 ktor-client-android 結果被坑了。」
implementation "io.ktor:ktor-client-android:$ktor_version"
試過之後發現 okhttp
和 cio
哪個都可以,老姐就選了老搭檔 okhttp。
implementation "io.ktor:ktor-client-okhttp:$ktor_version"
implementation "io.ktor:ktor-client-cio:$ktor_version"
因為之前的範例是寫死訊息,所以改參考另一個範例,可以將連線和送訊息分開: https://github.com/ktorio/ktor-samples/tree/master/generic/samples/chat/src/frontendMain/kotlin
private val wsClient = WsClient(HttpClient { install(WebSockets) }, ::notifyMessage)
private fun notifyMessage(message: String) {
//MutableLiveData update ui
}
class WsClient(private val client: HttpClient,
private val onReceive: (input: String) -> Unit) {
var session: WebSocketSession? = null
suspend fun connect() {
session = client.webSocketSession(
method = HttpMethod.Get,
host = "192.168.48.3",
port = 8080,
path = "/ws"
)
}
suspend fun send(message: String) {
session?.send(Frame.Text(message))
}
suspend fun receive(onReceive: (input: String) -> Unit) {
while (true) {
val frame = session?.incoming?.receive()
if (frame is Frame.Text) {
onReceive(frame.readText())
}
}
}
}
輸入訊息。
成功收到 server echo 。
別人傳的的訊息也能收到。
「太好了!老姐,妳做到了!」趕快稱讚老姐一下,連假日都在寫個人專案,這次應該能達到終點。
但老姐並沒有開心,而是不甚滿意的說:「如果用這個 library ,中途斷線有點被動⋯⋯,而且萬一將來不用 ktor ,可能要改程式碼。」
說著就直接打開 okhttp 官網,開始改裝。 https://square.github.io/okhttp/4.x/okhttp/okhttp3/-web-socket/
implementation 'com.squareup.okhttp3:okhttp:3.14.7'
為了能重用剛剛的介面,寫成同樣的外型。
private val wsClient = WsClient(
OkHttpClient.Builder()
.pingInterval(60, TimeUnit.SECONDS)
.build(), ::notifyMessage
)
private fun notifyMessage(message: String) {
//MutableLiveData update ui
}
class WsClient(
private val client: OkHttpClient,
private val onReceive: (input: String) -> Unit
) {
var session: WebSocket? = null
fun connect() {
val request = Request.Builder()
.url("ws:192.168.48.3:8080/ws")
.build()
client.newWebSocket(request, object:WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response?) {
session = webSocket
}
override fun onMessage(webSocket: WebSocket, text: String) {
onReceive(text)
}
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
}
override fun onClosing(
webSocket: WebSocket,
code: Int,
reason: String
) {
}
override fun onClosed(
webSocket: WebSocket,
code: Int,
reason: String
) {
}
override fun onFailure(
webSocket: WebSocket?,
t: Throwable,
response: Response?
) {
}
})
}
fun send(message: String) {
session?.send(message)
}
}
看到 App 呈現和剛剛一樣的成功結果,終於能放下心來。
我想了想,和老姐說:「多房間就後面再加,我們看看還沒有什麼有難度的功能先做一做。」
老姐大力贊成:「太好了,我也這麼想,畫面有好多要改,但是都可以緩緩,功能更重要。」
哼哼,我是怕妳一直寫相似的東西容易膩啊,都到這裡了,可不想功虧一簣。
而且老姐呀, ktor okhttp engine 裡面的 config block 是 OkHttpClient.Builder ,所以設置上不會差異太多,頂多是斷線和收更新的地方不同,和版本會比最新的 okhttp 慢 。至於 Android engine 不能用 ws protocol 肇因於裡面包的是 HttpURLConnection 。
https://ktor.io/docs/http-client-engines.html#jvm-and-android
本次鐵人賽的作品在放進更多內容後已經成書,書名是《老姐要用Kotlin寫專案:從 Server 到 Android APP 的開發生存日記》,歡迎購買唷。https://www.tenlong.com.tw/products/9789864348978