還記得便利貼專案做到哪了嗎?專案目前用的架構模式是 MVVM :Jetpack Compose 所做成的 View, BoardViewModel
還有 InMemoryNoteRepository
,其中間的資料更新機制是由 RxJava 來完成。但是,我們有寫任何的 subscribeOn
或者是 observeOn
嗎?完全沒有,對吧!所以目前來說,任何的程式碼,不管是 View, ViewModel 還是 Model 都是在 Main thread 上執行的。
由於之前所示範的都是在本地端執行的,所以沒有什麼太大的必要硬要去切換執行緒,如果寫了subscribeOn
或者是 observeOn
還有點多此一舉的感覺,對吧?但是今天就需要用到他們了,今天會使用 Firebase Firestore 來當作專案的資料儲存中心,在傳輸資料的過程中會需要連上網路,因此我們就需要在某些地方進行執行緒切換,才能確保程式運行無誤。
原本的 InMemoryNoteRepository
將會被 Firestore 的實作給取代,根據 SOLID principle 的 Liskov substitution principle ,在抽換實作時,不應該影響原有的行為,所以 BoardViewModel
與 View 層的所有程式碼都不會因為抽換實作而需要修改內容,這是非常重要的一件事,如果今天只是因為換了 Repository 的實作而讓 BoardViewModel
的運作機制變了,那就表示設計上是有問題的,職責不夠專一,可能就要考慮先重構,等到行為確定不會有問題時再換實作。
FireStore 是一個有彈性、易擴展的 NoSQL 雲端資料庫,可以用來儲存還有同步客戶端以及伺服器端的資料,該資料庫的特性為:靈活的數據結構、簡易查詢、實時更新、離線支持、為擴展而設計,以下列出幾個對於便利貼 APP 來說很方便的特性:
至於其餘特性想瞭解更多的,請查看 Firestore 官方文件:https://firebase.google.com/docs/firestore
Firebase 在官方文件中的繁體翻譯是:“火力地堡”,聽起來像是一個很厲害的防禦基地XD
在 Firebase console 中增加一個新專案,並在專案名稱中輸入“StickyNote”
在步驟二的時候選擇不啟用 Google Analytic,建立專案就完成了!
進入專案後,你將會在左邊的側邊欄上看到“Firestore Database”,點選它
接著再點擊畫面中間的“建立資料庫”
在左邊側邊欄點選“專案總覽”,然後在螢幕中間你將會看到一個 Android 小綠人的圖案:
點擊 Android 小綠人之後,輸入應用程式套件名稱,以我的例子是“com.yanbin.stickynote”,其他欄位暫時都不需要填,接著下載“google-service.json”,下載完之後將檔案放到根目錄底下。
使用 gradle 來新增 Firebase SDK:
apply plugin: 'com.android.application'
// Add this line
apply plugin: 'com.google.gms.google-services'
dependencies {
// Import the Firebase BoM & firestore
implementation platform('com.google.firebase:firebase-bom:28.4.0')
implementation "com.google.firebase:firebase-firestore"
}
最後點擊 Android studio 上的 Sync,大功告成!
如果不想因為實驗而弄壞資料庫的話,也可以考慮這個 Codelab 中教學的使用 Firebase Emulator:https://firebase.google.com/codelabs/firestore-android#0
接下來就試試看在 Firebase console 上新增資料吧,之後本地端就可以使用 SDK 來獲取這上面的資料,讓我們回顧一下資料格式是長怎樣的:
data class Note(
val id: String,
val text: String,
val position: Position,
val color: YBColor) {
companion object {
fun createRandomNote(): Note {
val randomColorIndex = Random.nextInt(YBColor.defaultColors.size)
val randomPosition = Position(Random.nextInt(-50, 50).toFloat(), Random.nextInt(-50, 50).toFloat())
val randomId = UUID.randomUUID().toString()
return Note(randomId, "Hello", randomPosition, YBColor.defaultColors[randomColorIndex])
}
}
}
data class Position(val x: Float, val y: Float) {
operator fun plus(other: Position): Position {
return Position(x + other.x, y + other.y)
}
}
data class YBColor(
val color: Long
) {
companion object {
val HotPink = YBColor(0xFFFF7EB9)
val Aquamarine = YBColor(0xFF7AFCFF)
val PaleCanary = YBColor(0xFFFEFF9C)
val Gorse = YBColor(0xFFFFF740)
val defaultColors = listOf(HotPink, Aquamarine, PaleCanary, Gorse)
}
}
每一個 Note 會有這些資料儲存在資料庫中:id, text, position, color。心裡有個底之後,我們就先來試試看 Firestore 是如何新增資料的:
點選新增集合,他要你輸入集合 ID,先在此輸入“Notes”,點選下一步。
下一個出現的是新增文件,點選自動產生的 ID,這裡的每一個文件將會對應到我們的資料結構: Note 。接下來我們將每個需要的欄位一一填入:
text
,類型選擇 string 不用改,至於值的部分就先填入 “Hello”為什麼位置不是使用 number 而是 string 呢?其實也沒有什麼不行...真要說原因的話,就是我之前在實作時以為 number 只能使用整數,後來查文件才知道 number 也可以是小數點,就將錯就錯了...但是也不是什麼大問題,最後需要的時候再改過來就行了,這邊是容易修改的技術細節。
最後我是選擇扁平的 position ,因為這樣比較單純而且在接資料時也相對簡單,新增完資料之後會像下面這樣子:
SDK 跟後台設定都已完成,明天會來進行資料串接。