使用 FirebaseUI 登录 Android

此 Codelab 是“使用 Kotlin 进行高级 Android 开发”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘课程的价值,但并不强制要求这样做。“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页列出了所有课程 Codelab。

简介

在构建 Android 应用时,支持用户登录可带来诸多好处。通过允许用户在您的应用中创建身份,您可以为他们提供更多与应用互动的机会。

借助个性化账号,用户可以自定义应用内体验、与其他用户互动,并且如果他们在其他设备(例如网页或新手机)上使用该应用,其数据可以持久保留和转移。

在此 Codelab 中,您将学习如何使用 FirebaseUI 库为应用提供登录支持的基础知识。除了许多其他功能之外,FirebaseUI 库还可让想要构建登录流程的开发者轻松完成这项工作,并为您处理用户账号管理工作。

您应当已掌握的内容

  • 有关如何构建 Android 应用的基础知识
  • LiveData 和 ViewModel

学习内容

  • 如何将 Firebase 添加到您的项目
  • 如何为 Android 应用提供登录支持
  • 如何查看应用的当前身份验证状态
  • 如何让用户退出账号

您将执行的操作

  • 使用 Firebase 控制台在应用中集成 Firebase。
  • 实现登录功能。
  • 在应用中为已登录的用户添加自定义设置。
  • 实现用户退出功能。

详细了解 LiveData 和 ViewModel

对于本 Codelab 中的应用,您需要对 LiveData 和 ViewModel 有基本的了解。如果您想简要了解这些概念,请阅读 LiveDataViewModel 概览。

您还可以学习“使用 Kotlin 开发 Android 应用”课程,了解您在此 Codelab 中会遇到的基本 Android 主题。该课程既有 Udacity 课程,也有 Codelabs 课程

在此 Codelab 中,您将构建一个显示有趣的 Android 事实的应用。更重要的是,应用将包含一个登录/退出按钮。当用户登录应用后,系统会向用户显示包含问候语的 Android 事实,以增加个性化色彩。

若要下载示例应用,您可以执行以下操作之一:

下载 Zip 文件

…或从命令行使用下列命令克隆 GitHub 代码库,然后切换到代码库的 start 分支:

$  git clone https://github.com/googlecodelabs/android-kotlin-login

重要提示:由于您要集成应用以使用 Firebase,因此初始应用需要进行一些设置才能构建和运行。您将在本 Codelab 的下一步中执行此操作。

第 1 步:创建 Firebase 项目

您必须先创建一个 Firebase 项目,并将其关联到您的 Android 应用,然后才能将 Firebase 添加到您的 Android 应用。

  1. Firebase 控制台中,点击添加项目
  2. 选择或输入项目名称。您可以随意命名项目,但最好选择与您要构建的应用相关的名称。
  3. 点击继续
  4. 您可以跳过设置 Google Analytics,然后选择暂不设置选项。
  5. 点击创建项目以完成 Firebase 项目的设置。

第 2 步:在 Firebase 中注册您的应用

现在,您已经创建了 Firebase 项目,接下来就可以向其中添加 Android 应用了。

  1. Firebase 控制台的项目概览页面的中心位置,点击 Android 图标以启动设置工作流。
  2. Android 软件包名称字段中输入您的应用的应用 ID。请务必输入应用在使用的 ID,因为在向 Firebase 项目注册应用后,您将无法添加或修改此值。
  1. “应用 ID”有时被称为“软件包名称”
  2. 在您的模块(应用级)Gradle 文件(通常是 app/build.gradle)中找到此应用 ID(如 ID:com.yourcompany.yourproject)。
  3. 输入调试签名证书 SHA-1。您可以在命令行终端中输入以下命令来生成此密钥。
keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android
  1. 点击注册应用

第 3 步:将 Firebase 配置文件添加到项目中

将 Firebase Android 配置文件添加到您的应用:

  1. 点击下载 google-services.json,获取 Firebase Android 配置文件 (google-services.json)。
  • 您可以随时再次下载 Firebase Android 配置文件
  • 请确保该配置文件名未附加其他字符,并且应仅命名为 google-services.json
  1. 将配置文件移到应用的模块(应用级)目录中。

第 4 步:配置 Android 项目以启用 Firebase 产品

  1. 如需在应用中启用 Firebase 产品,请将 google-services 插件添加到 Gradle 文件中。
  1. 在根级(项目级)Gradle 文件 (build.gradle) 中添加规则,以纳入 Google 服务插件。此外,请确认您拥有 Google 的 Maven 制品库。

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.0'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. 在您的模块(应用级)Gradle 文件(通常是 app/build.gradle)的末尾,添加一行代码。

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

第 4 步:添加 Firebase 依赖项

在此 Codelab 中,集成 Firebase 的主要原因是提供一种创建和管理用户的方式。为此,您需要添加一个 Firebase 库,以便实现登录功能。

  1. build.gradle (Module:app) 文件中添加以下依赖项,以便在应用中使用该 SDK。借助 firebase-auth SDK,您可以管理应用的已通过身份验证的用户。

app/build.gradle:

implementation 'com.firebaseui:firebase-ui-auth:5.0.0'
  1. 将项目与 Gradle 文件同步,确保您的应用拥有所有依赖项。如果系统未提示,请在 Android Studio 中或从工具栏中依次选择 File > Sync Project with Gradle Files

第 5 步:运行应用并检查代码

  1. 在模拟器或实体设备上运行应用,确保您的环境已成功设置,可以开始开发。

如果成功,您应该会在主屏幕上看到一条有趣的 Android 事实和左上角的登录按钮。点按登录按钮目前不会执行任何操作。

从总体上讲,这是一个包含多个 fragment 的单 activity 应用。MainFragment 包含您在下方屏幕上看到的所有界面。(在后续 Codelab 中,您将使用 LoginFragmentSettingsFragment。)

  1. 熟悉代码。请特别注意:
  • FirebaseUserLiveData 是您将实现的类,用于观察与应用关联的当前 Firebase 用户。您将使用 FirebaseAuth 实例作为入口点,以便在后续步骤中获取此用户信息。
  • MainFragmentLoginViewModel 相关联。LoginViewModel 是您将实现的类,以便利用 FirebaseUserLiveData 创建 authenticationState 变量。使用此 authenticationState 变量,MainFragment 随后可以观察该值,以便相应地更新界面。

在此步骤中,您将使用 Firebase 控制台设置您希望应用支持的身份验证方法。在此 Codelab 中,您将重点介绍如何让用户使用其提供的电子邮件地址或 Google 账号登录。

  1. 前往 Firebase 控制台。(注意:如果您仍处于“添加 Firebase”工作流程中,请点击左上角的 X 以返回控制台。
  2. 如果您尚未进入自己的项目,请选择您的项目。
  3. 打开左侧导航栏,然后依次选择开发 > 身份验证

  1. 选择顶部导航栏中的登录方法标签页。

  1. 点击电子邮件地址/密码行。
  2. 在弹出式窗口中,切换已启用开关,然后点击保存
  3. 同样,点击 Google 行。
  4. 切换已启用开关,输入项目支持电子邮件地址,然后点击保存

在此任务中,您将为用户实现登录功能。

  1. 打开 MainFragment.kt
  2. MainFragment 的布局中,注意 auth_button。目前,它尚未设置为处理任何用户输入。
  3. onViewCreated(), 中,向 auth_button 添加一个 onClickListener 以调用 launchSignInFlow()

MainFragment.kt

binding.authButton.setOnClickListener { launchSignInFlow() }
  1. MainFragment.kt 中查找 launchSignInFlow() 方法。目前包含 TODO
  2. 完成 launchSignInFlow() 函数,如下所示。

MainFragment.kt

private fun launchSignInFlow() {
   // Give users the option to sign in / register with their email or Google account.
   // If users choose to register with their email,
   // they will need to create a password as well.
   val providers = arrayListOf(
       AuthUI.IdpConfig.EmailBuilder().build(), AuthUI.IdpConfig.GoogleBuilder().build()

       // This is where you can provide more ways for users to register and 
       // sign in.
   )

   // Create and launch sign-in intent.
   // We listen to the response of this activity with the
   // SIGN_IN_REQUEST_CODE 
   startActivityForResult(
       AuthUI.getInstance()
           .createSignInIntentBuilder()
           .setAvailableProviders(providers)
           .build(),
       MainFragment.SIGN_IN_REQUEST_CODE
   )
}

这样一来,用户就可以使用电子邮件地址或 Google 账号注册和登录。如果用户选择使用电子邮件地址注册,那么他们创建的电子邮件地址和密码组合对于您的应用而言是唯一的。这意味着他们可以使用该电子邮件地址和密码组合登录您的应用,但不能使用相同的凭据登录任何其他受 Firebase 支持的应用。

  1. MainFragment.kt 中,您可以通过实现 onActivityResult() 方法来监听登录流程的结果,如下所示。由于您使用 SIGN_IN_REQUEST_CODE 启动了登录流程,因此您还可以通过过滤 SIGN_IN_REQUEST_CODE 何时传递回 onActivityResult() 来监听登录流程的结果。首先,添加一些日志语句,以了解用户是否已成功登录。

MainFragment.kt

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   super.onActivityResult(requestCode, resultCode, data)
   if (requestCode == SIGN_IN_REQUEST_CODE) {
       val response = IdpResponse.fromResultIntent(data)
       if (resultCode == Activity.RESULT_OK) {
           // User successfully signed in
           Log.i(TAG, "Successfully signed in user ${FirebaseAuth.getInstance().currentUser?.displayName}!")
       } else {
           // Sign in failed. If response is null the user canceled the
           // sign-in flow using the back button. Otherwise check
           // response.getError().getErrorCode() and handle the error.
           Log.i(TAG, "Sign in unsuccessful ${response?.error?.errorCode}")
       }
   }
}

您的应用现在应该能够处理用户注册和登录了!

  1. 运行应用,并验证点按登录按钮是否会显示登录界面。
  2. 现在,您可以使用电子邮件地址和密码登录,也可以使用 Google 账号登录。
  3. 登录后,界面不会发生任何变化(您将在下一步中实现界面更新),但如果一切正常,您应该会在完成注册流程后看到日志消息 Successfully signed in user ${your name}!
  4. 您还可以前往 Firebase 控制台,然后依次前往开发 > 身份验证 > 用户,以检查应用现在是否有一位注册用户。
  5. 请注意,当用户为您的应用创建账号时,此账号仅与您的应用相关联,而不会与任何使用 Firebase 进行登录的应用相关联。

在此任务中,您将实现根据身份验证状态更新界面。当用户登录后,您可以通过显示其名称来打造个性化的主屏幕。您还将更新登录按钮,以便在用户登录后显示为退出按钮。

  1. 打开已为您创建的 FirebaseUserLiveData.kt 类。您需要做的第一件事是提供一种方式,让应用中的其他类知道用户何时登录或退出。不过,由于 LiveData 的值未更新,该类目前不会执行任何操作。
  2. 由于您使用的是 FirebaseAuth 库,因此可以使用 FirebaseUser.AuthStateListener 回调(作为 FirebaseUI 库的一部分为您实现)来监听已登录用户的更改。每当用户登录或退出您的应用时,系统都会触发此回调。
  3. 请注意,FirebaseUserLiveData.kt 定义了 authStateListener 变量。您将使用此变量来存储 LiveData 的值。创建 authStateListener 变量是为了让您可以根据应用的状态正确开始和停止监听身份验证状态的变化。例如,如果用户将应用置于后台,则应用应停止监听身份验证状态变化,以防止任何潜在的内存泄漏。
  4. 更新 authStateListener,使 FirebaseUserLiveData 的值与当前 Firebase 用户相对应。

FirebaseUserLiveData.kt

private val authStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
   value = firebaseAuth.currentUser
}
  1. 打开 LoginViewModel.kt
  2. LoginViewModel.kt 中,基于您刚刚实现的 FirebaseUserLiveData 对象创建一个 authenticationState 变量。创建此 authenticationState 变量后,其他类现在可以通过 LoginViewModel 查询用户是否已登录。

LoginViewModel.kt

val authenticationState = FirebaseUserLiveData().map { user ->
   if (user != null) {
       AuthenticationState.AUTHENTICATED
   } else {
       AuthenticationState.UNAUTHENTICATED
   }
}
  1. 打开“MainFragment.kt.
  2. MainFragment.ktobserveAuthenticationState() 中,您可以使用刚刚在 LoginViewModel 中添加的 authenticationState 变量,并相应地更改界面。如果有已登录的用户,authButton 应显示退出

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }

                // TODO 2. If the user is logged in, 
                 // you can customize the welcome message they see by
                 // utilizing the getFactWithPersonalization() function provided

           }
           else -> {
               // TODO 3. Lastly, if there is no logged-in user, 
                // auth_button should display Login and
                //  launch the sign in screen when clicked.
           }
       }
   })
}
  1. 如果用户已登录,您还可以利用 MainFragment 中提供的 getFactWithPersonalization() 函数自定义用户看到的欢迎消息。

MainFragment.kt

binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
  1. 最后,如果没有已登录的用户(当 authenticationState 不是 LoginViewModel.AuthenticationState.AUTHENTICATED 时),auth_button 应显示登录,并在点击时启动登录界面。所显示的消息也不应包含个性化内容。

MainFragment.kt

binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener { launchSignInFlow() }
binding.welcomeText.text = factToDisplay

完成所有步骤后,最终的 observeAuthenticationState() 方法应类似于以下代码。

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
        // TODO 1. Use the authenticationState variable you just added 
         // in LoginViewModel and change the UI accordingly.
       when (authenticationState) {
            // TODO 2.  If the user is logged in, 
             // you can customize the welcome message they see by
             // utilizing the getFactWithPersonalization() function provided
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }
           }
           else -> {
                // TODO 3. Lastly, if there is no logged-in user, 
                 // auth_button should display Login and
                 // launch the sign in screen when clicked.
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. 运行应用。界面应根据用户是否已登录进行更新。如果一切正常,并且您已登录,主屏幕现在应该会显示您的名称,并显示一条 Android 趣闻。登录按钮现在还应显示退出

在此任务中,您将实现退出功能。

由于该应用允许用户登录,因此还应为用户提供退出登录的方式。以下示例展示了如何仅使用一行代码即可让用户退出登录:

AuthUI.getInstance().signOut(requireContext())
  1. 打开 MainFragment.kt
  2. MainFragment.ktobserveAuthenticationState() 中,添加退出逻辑,以便在有已登录的用户时,auth_button 函数能够正常运行。该方法的最终结果如下所示。

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)

               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   AuthUI.getInstance().signOut(requireContext())
               }
           }
           else -> {
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. 运行应用。
  2. 点按退出按钮,验证用户是否已退出登录,以及按钮的状态是否已更改为登录

您可以在此处找到已完成应用的最终版本:https://github.com/googlecodelabs/android-kotlin-login

在本 Codelab 中,您学习了以下内容:

  • 如何通过在 Gradle 文件中添加必要的依赖项并在 Firebase 控制台中设置项目,将 Firebase 添加到您的项目中。
  • 如何使用 FirebaseUI 库为应用实现登录功能,并指定您希望允许用户以何种方式登录。请注意,用户在您的应用中创建的任何账号都仅适用于您的应用,不会与所有利用 Firebase 实现登录功能的应用共享。
  • 如何使用 LiveData 观察应用的当前身份验证状态。
  • 如何让用户退出登录。

此 Codelab 介绍了如何为 Android 应用提供登录支持的基础知识。

在此 Codelab 中,您允许用户使用其电子邮件地址进行注册和登录。不过,借助 FirebaseUI 库,您还可以支持其他身份验证方法,例如使用电话号码登录。如需详细了解 FirebaseUI 库的功能以及如何利用其提供的其他功能,请参阅以下资源:

如需详细了解登录方面的最佳实践,请参阅以下其他资源:

Codelab:

Android 开发者文档:

视频:

如需本课程中其他 Codelab 的链接,请参阅“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页。