0Pricing
Android Academy · 课时

Cloud Firestore 基础

读取和写入实时数据

Cloud Firestore 基础 是 CoddyKit 上的免费 Android Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

什么是 Cloud Firestore?

Cloud Firestore 是 Firebase 提供的灵活且可扩展的 NoSQL 数据库。它将数据存储为归入集合的文档。

  • 文档是一组键值字段(类似 JSON 对象)。
  • 集合是用于存放文档的容器。
  • 文档可以包含子集合,从而形成树状结构。

Firestore 还提供实时更新功能,并且开箱即用地支持离线使用。

获取 Firestore 实例

通过 Firebase.firestore 访问数据库。从这里开始,您可以使用 collection("name") 访问集合,并使用 document("id") 访问具体文档。

创建引用的成本很低 — 引用描述的是一个位置,而不是数据本身。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore

val db = Firebase.firestore

val usersRef = db.collection("users")
val oneUser = db.collection("users").document("abc123")

设计数据模型

Firestore 可以很好地映射到 Kotlin 数据类。每个属性都会成为文档中的一个字段。

请为数据类提供一个无参构造函数(使用默认值即可实现),这样 Firestore 才能将文档反序列化回对象。

data class Note(
    val title: String = "",
    val body: String = "",
    val done: Boolean = false,
    val createdAt: Long = 0L
)

写入文档

使用 set 创建或覆盖文档。使用 add 让 Firestore 生成 ID,或者使用 document(id).set(...) 选择您自己的 ID。

这些调用会返回任务;使用协程时,您可以对它们调用 await()。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun addNote(note: Note): String {
    val ref = Firebase.firestore
        .collection("notes")
        .add(note)          // auto-generated ID
        .await()
    return ref.id
}

读取文档

get() 会读取一次文档。使用 toObject(Note::class.java) 将其转换为您的数据类。

请始终检查 exists() — 对不存在文档的引用会返回一个快照,只是该快照本身不存在。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun loadNote(id: String): Note? {
    val snapshot = Firebase.firestore
        .collection("notes")
        .document(id)
        .get()
        .await()
    return if (snapshot.exists()) snapshot.toObject(Note::class.java) else null
}

更新和删除

update 会修改指定字段,而不会影响文档的其他部分。delete 会彻底删除文档。

如果您希望执行部分写入,并在文档不存在时同时创建它,请使用 set(..., SetOptions.merge())。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun markDone(id: String) {
    Firebase.firestore.collection("notes").document(id)
        .update("done", true)
        .await()
}

suspend fun removeNote(id: String) {
    Firebase.firestore.collection("notes").document(id)
        .delete()
        .await()
}

查询集合

通过串联筛选条件来构建查询:whereEqualTo、whereGreaterThan、orderBy、limit 等。

查询会返回一个 QuerySnapshot;遍历其中的文档,将每个文档映射为您的模型。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun pendingNotes(): List<Note> {
    val snap = Firebase.firestore.collection("notes")
        .whereEqualTo("done", false)
        .orderBy("createdAt")
        .limit(20)
        .get()
        .await()
    return snap.documents.mapNotNull { it.toObject(Note::class.java) }
}

实时监听器

Firestore 的强大之处在于实时性。addSnapshotListener 会立即提供当前数据,之后自动推送每一次变化。

它会返回一个 ListenerRegistration — 完成后调用 remove(),停止接收更新。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore

val registration = Firebase.firestore.collection("notes")
    .whereEqualTo("done", false)
    .addSnapshotListener { snapshot, error ->
        if (error != null || snapshot == null) return@addSnapshotListener
        val notes = snapshot.toObjects(Note::class.java)
        println("Now have ${notes.size} pending notes")
    }

// Later: registration.remove()

将实时数据转换为 Flow

在 Compose 中,将 Firestore 公开为 Kotlin Flow 是惯用做法。callbackFlow 会将监听器回调桥接为流,而 awaitClose 会在停止收集时移除监听器。

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow

fun notesFlow(): Flow<List<Note>> = callbackFlow {
    val reg = Firebase.firestore.collection("notes")
        .addSnapshotListener { snap, err ->
            if (err == null && snap != null) {
                trySend(snap.toObjects(Note::class.java))
            }
        }
    awaitClose { reg.remove() }
}

在 Compose 中显示数据

使用 collectAsStateWithLifecycle() 收集流,并在 LazyColumn 中渲染它。每当 Firestore 发生变化时,列表都会自动更新 — 无需手动刷新。

@Composable
fun NotesScreen(viewModel: NotesViewModel) {
    val notes by viewModel.notes.collectAsStateWithLifecycle()

    LazyColumn {
        items(notes) { note ->
            ListItem(
                headlineContent = { Text(note.title) },
                supportingContent = { Text(note.body) }
            )
        }
    }
}

安全规则很重要

默认情况下,Firestore 处于锁定状态。安全规则决定谁可以读取或写入每个路径 — 规则在服务器上运行,客户端无法绕过。

一种常见规则是:用户只能访问以其身份验证 uid 为键的自身数据。

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}/{document=**} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }
  }
}

快速检查

您希望 Compose 列表在底层 Firestore 数据发生变化时立即更新,而无需手动刷新。应使用哪个 API?

回顾:Firestore

现在,您已经可以使用 Cloud Firestore 存储和同步数据:

  • 数据以文档形式存储在集合中;使用 Kotlin 数据类为其建模。
  • 使用 add/set 写入,使用 get 读取,使用 update/delete 修改。
  • 使用 whereEqualTo、orderBy、limit 进行查询。
  • 使用 addSnapshotListener(或 callbackFlow)为界面提供实时数据。
  • 使用以 request.auth.uid 为依据的安全规则保护数据。

下一步:即使应用已关闭,也能通过 Cloud Messaging 联系用户。

常见问题解答

「Cloud Firestore 基础」课时是免费的吗?

是的 — 「Cloud Firestore 基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「Cloud Firestore 基础」这节课中我会学到什么?

读取和写入实时数据 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「Cloud Firestore 基础」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 设置 Firebase
  2. Firebase 身份验证
  3. Cloud Firestore 基础
  4. 使用 Cloud Messaging 推送
← 返回 Android Academy