使用 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 课程,也有 Codelab 课程

在此 Codelab 中,您将构建一个显示有趣的 Android 知识的应用。更重要的是,该应用应具有登录/退出按钮。当用户登录应用时,显示的任何 Android 信息都将包含一条问候语,作为添加个性化个性化设置的一种方式。

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

下载 Zip 文件

... 或从命令行使用下列命令克隆 GitHub 代码库并切换到代码库的 start 分支:

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

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

第 1 步:创建 Firebase 项目

您需要先创建一个要关联到 Android 应用的 Firebase 项目,然后才能将 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 变量而将要实现的类。然后,MainFragment 可以使用此 authenticationState 变量观察该值,以便相应地更新界面。

在此步骤中,您将使用 Firebase 控制台设置您希望应用支持的身份验证方法。在此 Codelab 中,您将着重于让用户使用他们提供的电子邮件地址或 Google 帐号登录。

  1. 导航到 Firebase 控制台。(注意:如果您仍在添加 Firebase 工作流中,请点击左上角的 X 以返回控制台。
  2. 如果您尚未选择项目,请先选择您的项目。
  3. 打开左侧导航栏,选择开发 &gt 身份验证

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

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

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

  1. 打开 MainFragment.kt
  2. MainFragment 的布局中,注意 auth_button。当前未设置为处理任何用户输入。
  3. onViewCreated(), 中,将 onClickListener 添加到 auth_button 以调用 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. 运行应用,验证点按 Login(登录)按钮是否会调出登录屏幕。
  2. 您现在可以使用自己的电子邮件地址和密码或者您的 Google 帐号登录。
  3. 登录后,界面不会发生任何变化(您将在下一步中实现界面更新),但如果一切正常,您应该会在完成注册流程后看到日志消息 Successfully signed in user ${your name}!
  4. 您也可以转到 Firebase 控制台,然后导航到 Develop > Authentication > Users,以检查该应用现在是否有注册用户。
  5. 请注意,当用户为应用创建帐号时,此帐号仅与您的应用相关联,而不会绑定到任何使用 Firebase 登录功能的应用。

在此任务中,您将实现根据身份验证状态更新界面。在用户登录后,您可以通过显示他们的姓名来个性化其主屏幕。此外,当用户登录后,您需要将 Login(登录)按钮更新为 Logout(退出)按钮。

  1. 打开系统为您创建的 FirebaseUserLiveData.kt 类。首先,您需要提供一种方法,让应用中的其他类能够知道用户何时登录或退出。不过,该类还没有执行任何操作,因为没有更新 LiveData 的值。
  2. 由于您使用的是 FirebaseAuth 库,因此您可以通过在 FirebaseUI 库中为您实现的 FirebaseUser.AuthStateListener 回调来监听已登录用户的更改。此回调会在用户登录或退出您的应用时触发。
  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 应显示 Login,并在点击时启动登录屏幕。显示的消息也不应该经过个性化设置。

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 信息外,主屏幕还应按您的姓名问候。现在,Login(登录)按钮还应显示 Logout(退出)。

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

由于该应用允许用户登录,因此还应为用户提供退出登录的方法。以下示例说明如何使用一行代码将用户退出登录:

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. 点按 Logout(退出)按钮,验证用户是否已退出,按钮的状态是否变为 Login(登录)。

您可以在 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 着陆页。