iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0

Keyword: Ktor, Suspend Function
到Day11使用Ktor進行網路請求並且顯示在Android畫面的Code放在
KMMDay11


Ktor是十分強大的網路框架,可以幫助開發者迅速進行網路開發,我們借助其中一小部分功能,來進行網路Api的請求.

今天的範例Api ,我們使用 Cafe Nomad:咖啡廳遊牧民的Api,我常在咖啡廳工作,偶爾也會查一下這個網站.非常感謝.

https://cafenomad.tw/api/v1.2/cafes/{cityName}

網路請求的回傳形式如下,還蠻詳細的.

[{
    "id": "000703fe-cf8a-43c8-bd83-c90cfd61915f",
    "name": "蜂巢咖啡",
    "city": "taipei",
    "wifi": 0,
    "seat": 4.5,
    "quiet": 3,
    "tasty": 5,
    "cheap": 4,
    "music": 4,
    "url": "https://www.facebook.com/honeycombcafe2016/",
    "address": "新北市永和區永貞路214號",
    "latitude": "25.00409680",
    "longitude": "121.51528650",
    "limited_time": "no",
    "socket": "no",
    "standing_desk": "yes",
    "mrt": "永安市場站",
    "open_time": "雙週四公休,有變更另外公告"
  }
]

詳細文件可以到以下

https://cafenomad.tw/developers/docs/v1.2

使用Ktor

由於我們這次的Api資料是要雙平台一起共用的,也沒有根據平台不同而要特別處理的邏輯,也因此程式碼實作區塊放在shared模組中的commonMain中即可.
為了未來更好閱讀和修改,我推薦在shared底下建立以下結構

model/remote/ktor/

以後會將Ktor所用到的物件放在這裡

https://github.com/officeyuli/itHome2021/raw/main/day10/ktor%20path.jpg

首先先根據需要的回傳值建立一個對應的物件,這次的範例不會把整個物件接起來,挑幾個值比較重要的就好了,需要其他的值可以自己加入.

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable//要注意不要import錯了

@Serializable
data class CafeResponseItem(
    val id: String, //參數的名稱即是對應的json key
    val address: String, 
    val name: String,
    @SerialName("city") val cityName:String //如果json key跟參數不同,可以使用SerialName
)

然後建立一個interface,加入api path

interface CafeApi {
    suspend fun fetchCafeFromApi(city:String): List<CafeResponseItem>
}

注意這邊是suspend function,要跑在coroutine環境中.

然後再建立一個此interface的實作CafeApiImpl物件

先建立一個Ktor的HttpClient物件,HttpClient會負責管理連線,我們會根據需求調整HttpClient設定.

private val client = HttpClient {}

首先是JsonFeature的設定,這邊可以設定要用什麼解析器來進行Response的解析,例如常用的Gson等等.

這邊使用我們昨天引入的Kotlinx-Serializer解析器

install(JsonFeature){
    serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
	//由於我們沒有把每個key值接起來,所以還要額外設定忽略未知的key,ignoreUnknownKeys
    //注意前面的Json要使用Kotlinx-Serializer內的,有需多同名的Json,要小心不要用錯了.
}

然後是Logging的設定,目前就先簡單的印出來看看數據正確,我們之後會根據各平台來實作不同的Log

LogLevel也可以根據自己需要的程度調整

install(Logging){
    logger = object : Logger{
        override fun log(message: String) {
             println(" CafeApiImpl log: $message" )
            }
      }
     level = LogLevel.INFO
 }

最後是網路請求timeout設定

install(HttpTimeout){
            val timeout = 20000L
            connectTimeoutMillis = timeout
            requestTimeoutMillis = timeout
            socketTimeoutMillis = timeout
        }

最後整個HttpClient設定就會像這樣

private val client = HttpClient {
        install(JsonFeature){
            serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
        }
        install(Logging){
            logger = object : Logger{
                override fun log(message: String) {
                    println(" CafeApiImpl log: $message" )
                }
            }
            level = LogLevel.INFO
        }
        install(HttpTimeout){
            val timeout = 50000L
            connectTimeoutMillis = timeout
            requestTimeoutMillis = timeout
            socketTimeoutMillis = timeout
        }
    }

有了HttpClient後,就可以利用HttpClient發起網路請求,在使用HttpClient時,需要傳入一個HttpRequestBuilder

override suspend fun fetchCafeFromApi(city :String): List<CafeResponseItem>{
        return client.get<List<CafeResponseItem>>{
            getCafeListHttpBuilder(city)
        }
    }

cafeListHttpBuilder的內容如下,在HttpRequestBuilder中不只url,還能設定headers,body等等

private fun HttpRequestBuilder.getCafeListHttpBuilder(path: String) {
        url{
            if(path.isNotEmpty()){
                takeFrom("https://cafenomad.tw/api/v1.2/cafes/$path")
            }else{
                takeFrom("https://cafenomad.tw/api/v1.2/cafes/")
            }
        }
    }

這樣我們就完成了網路請求流程,明天會在Android上來顯示的網路請求的結果


上一篇
Day 9: Kotlin DSL 管理雜亂依賴的好幫手
下一篇
Day 11: 回到原生環境!在Android上展示Ktor資料!
系列文
挑戰 Kotlin Multiplatform Mobile 跨平台開發,透過共同的Kotlin模組同時打造iOS與Android應用!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言