使用 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模型表示在应用中访问数据的层。View 是界面层,对业务逻辑一无所知。ViewModel 是应用业务逻辑的位置,有时需要 ViewModel 调用 Model 层。

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

构建内容

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

Make it So 添加任务界面 “Make it So” 主屏幕

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

  • 使用电子邮件地址和密码对用户进行身份验证
  • 向 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. 点击下一步。由于 Firebase SDK 已包含在示例项目的 build.gradle 文件中,因此请点击下一步跳转到后续步骤
  5. 点击继续前往控制台以完成操作。

为使 Make it So 应用正常工作,在跳转到代码之前,您需要在控制台中完成两项操作:启用身份验证提供方并创建 Firestore 数据库。

设置身份验证

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

  1. Build 菜单中,选择 Authentication,然后点击 Get Started
  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 /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

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

运行应用

现在,您可以运行应用了!在 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 后,您需要确保 Sign in(登录)和 Create account(创建账号)选项都不见,因为用户现已通过身份验证。为此,我们让 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 控制台的“Authentication”信息中心内创建的新账号。

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 集合添加监听器。每个任务都由名为 tasks集合中的文档表示,并且每个文档都有一个名为 userId 的字段。请注意,如果 currentUser 的状态发生变化(例如,退出),系统会发出新的 Flow

现在,您需要让 TasksViewModel.kt 中的 Flow 反映的内容与服务中的内容相同:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最后,让 TasksScreens.kt 中的 composable function(表示界面)了解此流程并将其收集为状态。每当状态发生变化时,可组合函数都会自动重组自身,并向用户显示最新状态。将以下代码添加到 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. 性能监控

您要添加哪项功能?

性能非常重要,需要引起重视,因为如果应用性能不佳,并且用户使用应用完成简单任务所需的时间过长,他们很可能会放弃使用您的应用。因此,有时收集有关用户在应用中完成的特定历程的一些指标会很有用。为此,Firebase Performance Monitoring 提供了自定义轨迹。按照成就梦想中的后续步骤,添加自定义轨迹并衡量不同代码段的性能。

开始编码吧!

如果您打开 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 有多种用途,从远程更改应用外观到为不同的细分用户群配置不同的行为,不一而足。在此 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 中调用该函数。这是确保从一开始所有屏幕中都显示最新值的最佳方式。如果您稍后在用户执行某项操作时更改界面或应用的行为,将会给用户带来不好的体验!

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

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,在点击三点状图标时,您应该会看到三个选项;

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

7. 恭喜

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

您向一个完全使用 Jetpack Compose 为界面构建的 Android 应用添加了 Firebase Authentication、Performance Monitoring、Remote Config 和 Cloud Firestore,并使该应用能够融入推荐的 MVVM 架构!

深入阅读

参考文档