iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
0
Mobile Development

30天,從0開始用Kotlin寫APP系列 第 16

Day 16 | 在 Sandwish 中夾入 Retrofit - Part 2(半完結)

擴展 PirateService RESTful API 接口

昨天已經實作完 https://pokeapi.co/api/v2/pokemon?offset=0&limit=10 的 API 接口,因此今天就接續昨天,快快的把 https://pokeapi.co/api/v2/pokemon/1 API 接口實作完

1. 首先將 https://pokeapi.co/api/v2/pokemon/1 接口實作出來

@GET("pokemon/{id}")
suspend fun fetchPirateInfo(@Path("id") name: String): Call<PirateInfo>

2. 建立 PirateInfo Data Class

先到 data/model 下建立 PirateInfo.ktPirateInfo Data class 預期會存

  • Id
  • 名稱
  • 身高
  • 體重
  • 攻擊力

根據這些需求,我們可以開出以下 data class 的規格,其中因為沒有攻擊的數據,因此把 base_experience 當作是攻擊數據

@JsonClass(generateAdapter = true)
data class PirateInfo(
    @field:Json(name = "id") val id: Int,
    @field:Json(name = "name") val name: String,
    @field:Json(name = "height") val height: Int,
    @field:Json(name = "weight") val weight: Int,
    @field:Json(name = "base_experience") val attack: Int
) {
    fun getIdString(): String = String.format("#%03d", id)
    fun getWeightString(): String = String.format("%.1f KG", weight.toFloat() / 10)
    fun getHeightString(): String = String.format("%.1f M", height.toFloat() / 10)
    fun getAttackString(): String = "$attack/$maxAttack"

    companion object {
        const val maxAttack = 1000
    }
}

上面的 Code 除了有取得 Json 資料之外,還使用到了之前有提過的 String Template 來 Format 字串

到這邊接口的部份已經可以取得多個 Pirate 回來,並且還可以取得特定的 Pirate 資訊回來,接下來是要建立 Repository 來統一數據的出入口

導入 Sandwich 重構 PirateService

因為打 API 是屬於異步( Asynchronous )的範圍,因此如果自己要處理就要考慮到 TheardRoutine 的管理,不然可能會發生程式執行順序不對或是 Race Condition 的問題
那在研究 Pokedex 時,發現了作者寫了一個專門處理 Retrofit Request/Response 的 Library ,因此決定導入

Sandwich 介紹

Sandwish 的出發點是從 ApiResponse 開始,他是一個會依據 Retrofit 的 Response 狀態,給予相對應處理的 Interface
看到 Source code 裡面,可以看到 ApiResponse 是一個 sealed class
Class 裡面分成

  • Success :API Response 成功
  • Failed : API response 失敗,其中失敗又分成
    • Error : Server 回傳訊息不是正確的,e.g. internal server error.
    • Exception :Client 創建 Request 或處理 Response 時發生意外異常,e.g. network connection error.
sealed class ApiResponse<out T> {

  data class Success<T>(val response: Response<T>) : ApiResponse<T>() {
    ...
  }

  sealed class Failure<T> {
    data class Error<T>(val response: Response<T>) : ApiResponse<T>() {
      ...
    }

    data class Exception<T>(val exception: Throwable) : ApiResponse<T>() {
      ...
  }

透過這樣的方式,在處理 Response 上會更加準確與定義問題
另外,Sandwish 也很貼心的提供 SuccessErrorExceptionsuspend function ,在處理 Async 問題上可以多一種選擇

導入 Sandwish

1. 首先將 sandwich 加到 dependencies

// Sandwitch
implementation "com.github.skydoves:sandwich:1.0.4"

2. 更新 PirateService 接口的 Response Value

原本的 Return 格式是 Call ,那這邊就是把 CallApiResponse 取代

@GET("pokemon")
fun fetchPirateList(
    @Query("limit") limit: Int = 10,
    @Query("offset") offset: Int = 0
): ApiResponse<PirateResponse>

@GET("pokemon/{id}")
suspend fun fetchPirateInfo(
    @Path("id") name: String
): ApiResponse<PirateInfo>

3. 增加

fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl("https://pokeapi.co/api/v2/")
        .addConverterFactory(MoshiConverterFactory.create())
        .addCallAdapterFactory(CoroutinesResponseCallAdapterFactory())
        .build()
}

結語

今天補完了昨天沒完成的部份,並且簡單介紹了 Sandwich 這個專門處理 Retorfit 的 Library ,今天完成的量比較少,因為明天要講到 MVVM 的部份,算是比較複雜且需要比較詳細介紹的部份,因此想留到明天在一次介紹會比較完整

Reference


上一篇
Day 15 | Kotlin 中用 Retrofit 和 Moshi 捕捉神奇寶貝回來 - Part 1(起手式)
下一篇
Day 17 | 用 Kotlin 實作 MVVM 中的 Repository Layer
系列文
30天,從0開始用Kotlin寫APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言