Firebase Firestore

还记得便利贴专案做到哪了吗?专案目前用的架构模式是 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

FireStore 是一个有弹性、易扩展的 NoSQL 云端资料库,可以用来储存还有同步客户端以及服务器端的资料,该资料库的特性为:灵活的数据结构、简易查询、实时更新、离线支持、为扩展而设计,以下列出几个对於便利贴 APP 来说很方便的特性:

  • 灵活的数据结构:不像关连式资料库,该资料库的结构非常单纯,有表达多个文件概念的 Collection ,以及组合概念的 Document,最後则是最小单元的 Data 。这种结构有一个很方便的地方是,如果我们要临时增加新的资料格式的话,其实就只要在 Document 增加新的 Data 就好,不需要做 DB migration 。
  • 实时更新:同步资料一直是一个非常困难的问题,不同手机或网页之间有不一致的资料时,通常需要花不少间来处理,但现在有了 Firestore,我们就可以花更多心力在开发 App 上。
  • 离线支持:这也是一个非常棒的功能!为了能够支援离线功能,很多 App 的基础架构都包含了网路元件跟资料库元件,这些资料的整合通常是很无聊又繁琐,Firestore 预设就支援离线让我们不需要自己再实作本地端的资料库。

至於其余特性想了解更多的,请查看 Firestore 官方文件:https://firebase.google.com/docs/firestore

Firebase 在官方文件中的繁体翻译是:“火力地堡”,听起来像是一个很厉害的防御基地XD

建立 Firebase 专案

1. 新增专案

在 Firebase console 中增加一个新专案,并在专案名称中输入“StickyNote”

Screen Shot 2021-09-04 at 11.21.00 AM.png

在步骤二的时候选择不启用 Google Analytic,建立专案就完成了!

2. 启用 Firebase firestore

进入专案後,你将会在左边的侧边栏上看到“Firestore Database”,点选它

Screen Shot 2021-09-04 at 11.27.14 AM.png

接着再点击画面中间的“建立资料库”

Screen Shot 2021-09-04 at 11.28.01 AM.png

  1. 选择“以测试模式启动”
  2. Cloud Firestore 位置 → asia-east1 (这里是台湾,当然你想选其他位置也随意,不是正式版选哪一个都无所谓)
  3. 点击启用

3. 新增应用程序

在左边侧边栏点选“专案总览”,然後在萤幕中间你将会看到一个 Android 小绿人的图案:

Screen Shot 2021-09-04 at 11.36.16 AM.png

点击 Android 小绿人之後,输入应用程序套件名称,以我的例子是“com.yanbin.stickynote”,其他栏位暂时都不需要填,接着下载“google-service.json”,下载完之後将档案放到根目录底下。

Screen Shot 2021-09-04 at 11.39.11 AM.png

使用 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 是如何新增资料的:

Screen Shot 2021-09-04 at 2.48.39 PM.png

点选新增集合,他要你输入集合 ID,先在此输入“Notes”,点选下一步。

Screen Shot 2021-09-04 at 2.50.02 PM.png

下一个出现的是新增文件,点选自动产生的 ID,这里的每一个文件将会对应到我们的资料结构: Note 。接下来我们将每个需要的栏位一一填入:

  • id : 使用文件 ID 即可,这边不需要为了它再建立额外的栏位
  • text:在栏位中填入 text ,类型选择 string 不用改,至於值的部分就先填入 “Hello”
  • position:在我们的 Model 中这是一个有 x 跟 y 的资料结构,不是一个 primitive type ,在这边要怎麽填入呢?有两种方式:新增一个 Map 类型的栏位,然後在下一个阶层中个别新增 x 跟 y 两个类型为 string 栏位,如下图所示。或是将 position 拉平,直接新增两个 string 的栏位。

为什麽位置不是使用 number 而是 string 呢?其实也没有什麽不行...真要说原因的话,就是我之前在实作时以为 number 只能使用整数,後来查文件才知道 number 也可以是小数点,就将错就错了...但是也不是什麽大问题,最後需要的时候再改过来就行了,这边是容易修改的技术细节。

Screen Shot 2021-09-04 at 3.37.48 PM.png

  • color:栏位填上 color ,类型选择 number ,值就填上 4294901660

最後我是选择扁平的 position ,因为这样比较单纯而且在接资料时也相对简单,新增完资料之後会像下面这样子:

Screen Shot 2021-09-04 at 3.17.24 PM.png

SDK 跟後台设定都已完成,明天会来进行资料串接。

补充资料:


<<:  想要爬个资料也困难重重

>>:  D-15.Rspec 从零开始写测试(三) shoulda-matchers && Distribute Candies

用 cv 2 、tkinter 实现选择路径打开照片并显示照片、照片直方图 histogram

实现思维: 一、用 tkinker file ask 方法配合按钮,执行按钮就开始选择路径照片, 并...

L2TP最不可能用於加密 VPN 连接中的数据

-VPN 访问(来源:ActForNet) VPN 是一种通过隧道连接节点的虚拟网路。L2F、PP...

前端工程日记 30日 名片设计

如图 pancode: div 设计成 各种形状 三角形。五角形 六角形 的方法 制作参考引用 ht...

阅读.evtx文件--关於从16进位看事件纪录这回事

事情来自某天我在找资料的过程中,看到有些大大提供了事件纪录档的文本说明,所以今天要来试着阅读.evt...

LeetCode 双刀流: 90. Subsets II

90. Subsets II 今天挑选了「90. Subsets II」的题目,这是一道类似「排列...