使用 Firebase 和 Jetpack Compose 构建 Android 应用

1. 简介

上次更新时间:2022 年 11 月 16 日

使用 Firebase 和 Jetpack Compose 构建 Android 应用

在此 Codelab 中,您将构建一个名为 Make It So 的 Android 应用。此应用的界面完全使用 Jetpack Compose 构建,后者是 Android 用于构建原生界面的现代工具包,它非常直观,与编写 .xml 文件并将其绑定到 activity、fragment 或 view 相比,所需的代码更少。

要了解 Firebase 和 Jetpack Compose 的协同效果,首先要了解新式 Android 架构。良好的架构可清晰地呈现组件的组织方式以及它们之间的通信方式,从而使系统易于理解、开发和维护。在 Android 世界中,推荐的架构称为 Model - View - ViewModel模型表示应用中访问数据的层。视图是界面层,不应了解任何业务逻辑。ViewModel 是应用业务逻辑的地方,有时需要 ViewModel 调用 Model 层。

我们强烈建议您阅读这篇文章,了解如何将 Model - View - ViewModel 应用于使用 Jetpack Compose 构建的 Android 应用,因为这样可以更轻松地理解代码库,并更轻松地完成后续步骤。

构建内容

Make It So 是一款简单的待办事项列表应用,可让用户添加和修改任务、添加标记、优先级和截止日期,以及将任务标记为已完成。下图显示了此应用的两个主要页面:任务创建页面和包含已创建任务列表的主页面。

使其成为“添加任务”界面 设为 So Home 界面

您将添加此应用中缺少的一些功能:

  • 使用电子邮件和密码对用户进行身份验证
  • 向 Firestore 集合添加监听器,并使界面对更改做出反应
  • 添加自定义轨迹以监控应用中特定代码的性能
  • 使用 Remote Config 创建功能开关,并使用分阶段发布来启动该开关

学习内容

  • 如何在现代 Android 应用中使用 Firebase Authentication、Performance Monitoring、Remote Config 和 Cloud Firestore
  • 如何使 Firebase API 适应 MVVM 架构
  • 如何在 Compose 界面中反映使用 Firebase API 所做的更改

所需条件

2. 获取示例应用并设置 Firebase

获取示例应用的代码

从命令行克隆 GitHub 代码库:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

创建 Firebase 项目

  1. 使用您的 Google 账号登录 Firebase 控制台
  2. 点击相应按钮以创建新项目,然后输入项目名称(例如 Compose Firebase codelab)。
  3. 点击继续
  4. 如果看到相关提示,请查看并接受 Firebase 条款,然后点击继续
  5. (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
  6. 在此 Codelab 中,您需要使用 Google Analytics 才能通过 Remote Config 使用高级定位选项,因此请确保 Google Analytics 选项的开关处于开启状态。按照屏幕上的说明设置 Google Analytics。
  7. 点击创建项目,等待项目完成预配,然后点击继续

将 Android 应用添加到 Firebase 项目

在 Firebase 项目中,您可以注册不同的应用:Android 应用、iOS 应用、Web 应用、Flutter 应用和 Unity 应用。

选择 Android 选项,如图所示:

Firebase 项目概览

然后按以下步骤操作:

  1. 输入 com.example.makeitso 作为软件包名称,还可以选择输入昵称。在本 Codelab 中,您无需添加调试签名证书。
  2. 点击下一步以注册您的应用并访问 Firebase 配置文件。
  3. 点击下载 google-services.json 下载配置文件,并将其保存到 make-it-so-android/app 目录中。
  4. 点击下一步。由于示例项目中的 build.gradle 文件已包含 Firebase SDK,因此点击下一步即可跳至后续步骤
  5. 点击继续前往控制台以完成操作。

为了让 Make it So 应用正常运行,您需要在控制台中执行两项操作,然后再开始编写代码:启用身份验证提供方并创建 Firestore 数据库。

设置身份验证

首先,我们启用身份验证,以便用户可以登录应用:

  1. 构建菜单中,选择身份验证,然后点击开始
  2. 登录方法卡片中,选择电子邮件地址/密码,然后启用该方法。
  3. 接下来,点击添加新提供方,然后选择并启用匿名

设置 Cloud Firestore

接下来,设置 Firestore。您将使用 Firestore 存储已登录用户的任务。每个用户都将在数据库的集合中获得自己的文档

  1. 在 Firebase 控制台的左侧面板中,展开构建,然后选择 Firestore 数据库
  2. 点击创建数据库
  3. 数据库 ID 保留为 (default)
  4. 为数据库选择一个位置,然后点击下一步
    对于真实应用,您需要选择靠近用户的位置。
  5. 点击以测试模式开始。阅读有关安全规则的免责声明。
    在本部分的后续步骤中,您将添加安全规则来保护您的数据。在没有为数据库添加安全规则的情况下,请不要公开分发或公开应用。
  6. 点击创建

我们花点时间为 Firestore 数据库构建强大的安全规则。

  1. 打开 Firestore 信息中心,然后前往规则标签页。
  2. 将安全规则更新为如下所示:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /tasks/{document} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null
        && resource.data.userId == request.auth.uid
        && request.data.userId == resource.data.userId;
    }
  }
}

这些规则基本上表示,应用的任何已登录用户都可以在任何集合中为自己创建文档。创建文档后,只有创建该文档的用户才能查看、更新或删除该文档。

运行应用

现在,您可以运行应用了!在 Android Studio 中打开 make-it-so-android/start 文件夹,然后运行应用(可以使用 Android 模拟器或真实的 Android 设备来完成)。

3. Firebase Authentication

您要添加哪项功能?

Make It So 示例应用的当前状态下,用户无需先登录即可开始使用该应用。它使用匿名身份验证来实现此目的。不过,匿名账号不允许用户在其他设备上或未来的会话中访问自己的数据。虽然匿名身份验证对于热初始配置很有用,但您应始终为用户提供转换为其他登录方式的选项。鉴于此,在此 Codelab 中,您将向 Make It So 应用添加电子邮件地址和密码身份验证。

开始编码吧!

用户输入电子邮件地址和密码创建账号后,您需要向 Firebase Authentication API 请求电子邮件凭据,然后将新凭据与匿名账号相关联。在 Android Studio 中打开 AccountServiceImpl.kt 文件,然后更新 linkAccount 函数,使其如下所示:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

现在,打开 SignUpViewModel.kt,并在 onSignUpClick 函数的 launchCatching 代码块内调用服务 linkAccount 函数:

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

首先,它会尝试进行身份验证,如果调用成功,则会继续前往下一个界面 (SettingsScreen)。由于您是在 launchCatching 块内执行这些调用,因此如果第一行发生错误,系统会捕获并处理该异常,而根本不会到达第二行。

重新打开 SettingsScreen 后,您需要确保登录创建账号的选项已消失,因为用户现在已经过身份验证。为此,我们让 SettingsViewModel 监听当前用户的状态(可在 AccountService.kt 中获取),以检查账号是否为匿名账号。为此,请更新 SettingsViewModel.kt 中的 uiState,使其如下所示:

screens/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

您需要做的最后一件事是更新 SettingsScreen.kt 中的 uiState,以收集 SettingsViewModel 发出的状态:

screens/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

现在,每当用户发生变化时,SettingsScreen 都会重新组合自身,以根据用户的新身份验证状态显示选项。

是时候测试了!

运行 Make it So,然后点击屏幕右上角的齿轮图标,前往设置。然后,点击“创建账号”选项:

“Make it So”设置界面 Make it So 注册界面

输入有效的电子邮件地址和安全系数高的密码,以创建账号。该功能应该可以正常使用,并且您应该会被重定向到设置页面,其中会显示两个新选项:退出账号和删除账号。点击“用户”标签页,您可以在 Firebase 控制台的“身份验证”信息中心内查看新创建的账号。

4. Cloud Firestore

您要添加哪项功能?

对于 Cloud Firestore,您将向存储表示 Make it So 中显示的任务的文档的 Firestore 集合添加监听器。添加此监听器后,您将收到对此集合所做的每次更新。

开始编码吧!

更新 StorageServiceImpl.kt 中提供的 Flow,使其如下所示:

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

此代码正在根据 user.id 向任务集合添加监听器。每个任务都由名为 tasks 的集合中的一个文档表示,并且每个文档都有一个名为 userId 的字段。请注意,如果 currentUser 的状态发生变化(例如,通过退出账号),系统会发出新的 Flow

现在,您需要使 TasksViewModel.kt 中的 Flow 与服务中的相同:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最后,让 TasksScreens.kt 中的 composable function(表示界面)了解此 flow 并将其作为状态进行收集。每次状态发生变化时,可组合函数都会自动重组自身,并向用户显示最新状态。将以下内容添加到 TasksScreen composable function 中:

screens/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

可组合函数获得对这些状态的访问权限后,您可以更新 LazyColumn(用于在屏幕上显示列表的结构),使其如下所示:

screens/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

是时候测试了!

为了测试该功能是否正常运行,请使用该应用添加新任务(点击屏幕右下角的添加按钮)。创建完任务后,该任务应会显示在 Firestore 控制台的 Firestore 集合中。如果您在其他设备上使用同一账号登录 Make it So,则可以修改待办事项,并实时查看这些待办事项在所有设备上的更新情况。

5. Performance Monitoring

您要添加哪项功能?

性能非常重要,需要密切关注,因为如果性能不佳,用户很可能会放弃使用您的应用,而且他们会花费太多时间才能完成简单的任务。因此,有时收集用户在应用中完成特定历程的相关指标会很有用。为了帮助您实现这一点,Firebase Performance Monitoring 提供了自定义轨迹。按照以下步骤添加自定义轨迹,并衡量 Make it So 中不同代码段的性能。

开始编码吧!

如果您打开 Performance.kt 文件,会看到一个名为 trace 的内嵌函数。此函数会调用 Performance Monitoring API 来创建自定义轨迹,并将轨迹名称作为参数传递。您看到的另一个参数是您要监控的代码块。为每个轨迹收集的默认指标是完成运行所需的时间:

model/service/Performance.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

您可以选择代码库中您认为需要衡量的部分,并向其中添加自定义跟踪记录。以下示例展示了如何向本 Codelab 中之前(在 AccountServiceImpl.kt 中)看到的 linkAccount 函数添加自定义轨迹:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

现在该您亲自试试了!向 Make it So 应用添加一些自定义轨迹,然后继续下一部分,测试它是否按预期运行。

是时候测试了!

添加完自定义轨迹后,运行应用并确保多次使用要衡量的功能。然后前往 Firebase 控制台,并转到性能信息中心。在屏幕底部,您会看到三个标签页:网络请求自定义轨迹屏幕渲染

前往自定义轨迹标签页,检查您在代码库中添加的轨迹是否显示在此处,以及您是否可以看到执行这些代码段通常需要多长时间。

6. Remote Config

您要添加哪项功能?

Remote Config 的应用场景非常广泛,从远程更改应用的外观到为不同的细分用户群配置不同的行为,都可以使用 Remote Config 实现。在此 Codelab 中,您将使用 Remote Config 创建一个功能开关,用于在 Make it So 应用中显示或隐藏新的修改任务功能。

开始编码吧!

您需要做的第一件事是在 Firebase 控制台中创建配置。为此,您需要前往 Remote Config 信息中心,然后点击添加参数按钮。根据下图填写各个字段:

“Remote Config - 创建参数”对话框

填写完所有字段后,您可以点击保存按钮,然后点击发布。现在,参数已创建并可供您的代码库使用,您需要向应用添加用于提取新值的代码。打开 ConfigurationServiceImpl.kt 文件并更新以下两个函数的实现:

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

第一个函数从服务器提取值,并在应用启动时立即在 SplashViewModel.kt 中调用。这是确保从一开始所有屏幕上都显示最新值的最佳方式。如果您在用户正在执行某项操作时更改界面或应用的行为,则会带来糟糕的用户体验!

第二个函数返回的是您刚刚在控制台中为参数发布的布尔值。您需要在 TasksViewModel.kt 中检索此信息,方法是将以下代码添加到 loadTaskOptions 函数中:

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

您正在第一行中检索值,并使用该值在第二行中加载任务项的菜单选项。如果值为 false,则表示菜单中不会包含修改选项。现在您已经有了选项列表,接下来需要让界面正确显示该列表。使用 Jetpack Compose 构建应用时,您需要查找声明 TasksScreen 界面外观的 composable function。因此,请打开 TasksScreen.kt 文件并更新 LazyColum 以指向 TasksViewModel.kt 中提供的选项:

screens/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

TaskItem 是另一个 composable function,用于声明单个任务的界面应如何显示。每项任务都有一个菜单,其中包含多个选项,当用户点击任务末尾的三点状图标时,系统会显示该菜单。

是时候测试了!

现在,您可以运行应用了!检查您使用 Firebase 控制台发布的值是否与应用的行为一致:

  • 如果为 false,则点击三点状图标时,您应该只会看到两个选项;
  • 如果为 true,您应该会在点击三点状图标时看到三个选项;

尝试在管理中心内更改该值几次,然后重启应用。使用 Remote Config 在应用中发布新功能就是这么简单!

7. 恭喜

恭喜,您已成功使用 Firebase 和 Jetpack Compose 构建了一个 Android 应用!

您已向完全使用 Jetpack Compose 构建的 Android 应用添加了 Firebase Authentication、Performance Monitoring、Remote Config 和 Cloud Firestore,并使其符合建议的 MVVM 架构!

深入阅读

参考文档