iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
0
Mobile Development

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

Day 17 | 用 Kotlin 實作 MVVM 中的 Repository Layer

  • 分享至 

  • xImage
  •  

資料已經讀進來,那麼接下來要 Follow MVVM 的架構和資料溝通方法

接下來教學主體都會圍繞著這張圖

Repository Pattern

當資料的來源有多個時,使用 Repository Pattern 來處理算是滿常見的操作,透過 Repository 層接收多個來源的資料,然後開放特定的接口釋出資料,當外部物件操作資料時只能透過這些接口來存取,例如上面那張圖的資料來源就有兩個

  • Webservice
  • Local Database (SQLite)

那麼 ViewModel 需要操作資料就必須透過 Repository 的接口

開始撰寫 Repository Layer

那該要怎麼開始呢?
依照我目前的經驗是只要和異步工作有關的,那應該都會在 Repostirory 層處理,因為不論是從 DB 或是 Server 取資料,都是需要花時間去等待資料回來的,因此在這樣的前提下我們可以開始撰寫 Retrofit 的 Repository

PirateList Repository

那首先先來處理 PirateList 的部份,那根據昨天和前天完成的 Code ,今天就是要把他們搬到 Repository 層,然後在把異步的部份處理的更完善

1. 建立 PirateListRepository.kt

在 data/repository 中建立 PirateListRepositroy.kt 的檔案

2. 實作 PirateService

前天在 MainActivity 有簡單的驗證 Retrofit 是可行的,那時是先實作 PirateService,因此這邊的概念也是一樣,要先把 PirateService 實作起來,因此在 Class 中先加入下面的 Code

private val pirateService: PirateService =
        NetworkManager.provideRetrofit(NetworkManager.provideOkHttpClient()).create(PirateService::class.java)

3. 建立 Suspend Function 非同步取得資料

Kotlin 本身有支援協程( Coroutine ),那我們可以在函式前面加上 Suspend 裝飾詞,告訴 Compiler 這個函式是允許函式被暫停( Suspended ),並在之後再回復( Resumed )執行,那會有這樣的作法就是不希望一個 Job 鎖住太久的 CPU 資源,適當的釋放資源讓其他 Job 也有機會被同時完成,更詳細的介紹會在下面補上

因此這邊就要先創建一個 suspend fun 並取名為 fetchPirateList,其中可以注意到

  • fetchPirateList 帶了兩個 function 當作參數,目的是要做 Callback
  • 使用到了 flow ,flow 是 kotlinx-coroutines 支援的異步方法,很多人會拿他和 RxJava 做比較,因為他們要處理的問題是相同的
  • 最後一行是 Dispatchers.IO ,告訴 flow 裏面的工作需要跑在 worker thread 上,通常會用來執行比較耗時的 Job
suspend fun fetchPirateList(
        onSuccess: () -> Unit,
        onError: (String) -> Unit
    ) = flow {
        val response = pirateService.fetchPirateList()
        response.suspendOnSuccess {
            if (data != null) {
                val pirates: List<Pirate> = data?.results ?: listOf()
                emit(pirates)
                onSuccess()
            }
        }.onError {
            onError(message())
        }.onException {
            onError(message())
        }
    }.flowOn(Dispatchers.IO)

因此完整看起來會是這樣

4. 導入 Flow

那上面沒有提到的是 Flow Library 需要加到 dependencies 才能使用

dependencies {
...

// kotlinx.coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'

...
}

5. 建立 PirateDetailRepository.kt

那同樣的作法我們可套用到 fetchPirateInfo 中
那這邊可以注意到一件事,是我採用了 safe cast 和 Elvis Operator 做 data 與 PirateInfo 之間的轉型,這樣可以確保不會有 null 的發生,頂多建出一個 id = 0 的 PirateInfo 物件

結語

今天介紹了 Repository 大概會處理哪些東西,以及大致上架構會長怎樣,那目前應該只需要這兩個 Repository ,如果之後還需要其他 Repository 會在另外建立
今天寫完也讓我找到明天要介紹的題目就是 Coroutine ,這也是我近期一直鬼打牆的部份,剛好可以透過這的整理看能不能突破以前自己一直沒搞清處的部份

Reference


上一篇
Day 16 | 在 Sandwish 中夾入 Retrofit - Part 2(半完結)
下一篇
Day 18 | Kotlin 中處理異步的好伙伴 - Coroutine
系列文
30天,從0開始用Kotlin寫APP 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言