使用 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 项目

您需要做的第一件事是前往 Firebase 控制台,然后点击“+ 添加项目”按钮来创建 Firebase 项目,如下所示:

Firebase 控制台

按照屏幕上的步骤完成项目创建。

将 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 有多种使用情形,从远程更改应用的外观到为不同的细分用户群配置不同的行为,不一而足。在此 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 构建界面并符合建议的 MVVM 架构的 Android 应用添加了 Firebase Authentication、Performance Monitoring、Remote Config 和 Cloud Firestore!

深入阅读

参考文档