使用 FirebaseUI 登入 Android

這個程式碼研究室是「Android Kotlin 進階功能」課程的一部分。如果您按部就班完成每一堂程式碼研究室課程,就能充分體驗到本課程的價值,但這不是強制要求。如要查看所有課程程式碼研究室,請前往 Android Kotlin 進階功能程式碼研究室登陸頁面

簡介

建構 Android 應用程式時,支援使用者登入功能可帶來許多好處。允許使用者在應用程式中建立身分,就能提供更多與應用程式互動的方式。

有了個人化帳戶,使用者就能自訂應用程式內體驗、與其他使用者互動,以及在其他裝置 (例如網頁或新手機) 上使用應用程式時,保留及轉移資料。

在本程式碼研究室中,您將瞭解如何使用 FirebaseUI 程式庫,為應用程式提供登入支援服務。FirebaseUI 程式庫可讓開發人員輕鬆建構登入流程,並代為管理使用者帳戶。

必備知識

  • 如何建構 Android 應用程式的基礎知識
  • LiveData 和 ViewModel

課程內容

  • 如何將 Firebase 新增至專案
  • 如何支援 Android 應用程式的登入功能
  • 如何觀察應用程式目前的驗證狀態
  • 如何登出使用者

學習內容

  • 使用 Firebase 控制台將 Firebase 整合至應用程式。
  • 實作登入功能。
  • 為已登入的使用者在應用程式中新增自訂項目。
  • 實作使用者登出功能。

進一步瞭解 LiveData 和 ViewModel

如要使用本程式碼研究室中的應用程式,您必須瞭解 LiveData 和 ViewModel 的基本概念。如要簡要瞭解這些概念,請參閱 LiveDataViewModel 總覽。

您也可以完成「使用 Kotlin 開發 Android 應用程式」課程,瞭解本程式碼研究室中會遇到的 Android 基礎主題。這門課程提供 Udacity 課程程式碼研究室課程

在本程式碼研究室中,您將建構一個顯示有趣 Android 知識的應用程式。更重要的是,應用程式會顯示「登入/登出」按鈕。使用者登入應用程式後,系統顯示的任何 Android 知識都會包含對使用者的問候語,增添個人化體驗。

下載範例應用程式,方法如下:

下載 ZIP 檔

... 或使用下列指令從指令列複製 GitHub 存放區,然後切換至存放區的 start 分支:

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

重要事項:由於您要整合應用程式來使用 Firebase,因此必須先設定入門應用程式,才能建構及執行該應用程式。您將在本程式碼研究室的下一個步驟中執行這項操作。

步驟 1:建立 Firebase 專案

將 Firebase 加入 Android 應用程式前,請先建立要連結至該 Android 應用程式的 Firebase 專案。

  1. Firebase 控制台中,按一下「新增專案」
  2. 選取或輸入專案名稱。您可以為專案命名,但建議選擇與您建構的應用程式相關的名稱。
  3. 按一下「繼續」
  4. 您可以略過設定 Google Analytics,然後選擇「暫時不要」選項。
  5. 按一下「建立專案」,完成 Firebase 專案設定。

步驟 2:向 Firebase 註冊應用程式

建立 Firebase 專案後,即可加入 Android 應用程式。

  1. Firebase 控制台的專案總覽頁面中間,按一下「Android」Android圖示來啟動設定工作流程。
  2. 在「Android 套件名稱」欄位中,輸入應用程式的應用程式 ID。請務必輸入應用程式使用的 ID,因為向 Firebase 專案註冊應用程式後,就無法新增或修改這個值。
  1. 應用程式 ID 有時也稱為套件名稱
  2. 在模組 (應用程式層級) Gradle 檔案中找出這個應用程式 ID,通常是 app/build.gradle (ID 範例:com.yourcompany.yourproject)。
  3. 輸入偵錯簽署憑證 SHA-1。您可以在指令列終端機中輸入下列指令,產生這項金鑰。
keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android
  1. 按一下 [Register app] (註冊應用程式)

步驟 3:將 Firebase 設定檔新增至專案

將 Firebase Android 設定檔新增至應用程式:

  1. 按一下「Download 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 依附元件

在本程式碼研究室中,整合 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 知識,且左上角會顯示登入按鈕。輕觸登入按鈕目前不會執行任何動作。

整體來說,這款應用程式是具有多個片段的單一活動應用程式。MainFragment 包含下方畫面顯示的所有 UI。(您會在後續的程式碼研究室中處理 LoginFragmentSettingsFragment)。

  1. 熟悉程式碼。請特別注意以下事項:
  • FirebaseUserLiveData 是您要實作的類別,用來觀察與應用程式相關聯的目前 Firebase 使用者。您會在後續步驟中使用 FirebaseAuth 例項做為進入點,取得這項使用者資訊。
  • MainFragmentLoginViewModel 相關聯。LoginViewModel 是您要實作的類別,以便使用 FirebaseUserLiveData 建立 authenticationState 變數。使用這個 authenticationState 變數時,MainFragment 就能觀察該值,並據此更新 UI。

在這個步驟中,您將使用 Firebase 控制台設定應用程式要支援的驗證方法。在本程式碼研究室中,您將著重於讓使用者透過提供的電子郵件地址或 Google 帳戶登入。

  1. 前往 Firebase 控制台。(注意:如果仍在「新增 Firebase」工作流程中,請按一下左上角的「X」X,返回控制台。
  2. 如果尚未進入專案,請選取專案。
  3. 開啟左側導覽面板,然後選取「Develop」>「Authentication」

  1. 選取頂端導覽列的「登入方式」分頁標籤。

  1. 按一下「電子郵件/密碼」列。
  2. 在彈出式視窗中切換「Enabled」(啟用狀態),然後按一下「Save」(儲存)
  3. 同樣地,點選「Google」列。
  4. 切換「Enabled」(啟用狀態),輸入「Project support email」(專案支援電子郵件地址),然後按一下「Save」(儲存)

在這項工作中,您將為使用者實作登入功能。

  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 控制台,依序點選「開發」>「驗證」>「使用者」,確認應用程式現在有一位已註冊使用者。
  5. 請注意,使用者為您的應用程式建立帳戶時,該帳戶只會與您的應用程式連結,不會與任何使用 Firebase 登入功能的應用程式連結。

在這項工作中,您將實作根據驗證狀態更新 UI 的功能。使用者登入後,您可以顯示他們的名稱,打造個人化的主畫面。使用者登入後,您也會將「登入」按鈕更新為「登出」按鈕。

  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 變數,並相應地變更 UI。如有已登入的使用者,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. 執行應用程式。UI 應會根據使用者是否登入而更新。如果一切正常且您已登入,主畫面現在應該會顯示您的名字,並提供 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. 輕觸「登出」按鈕,確認使用者已登出,且按鈕狀態變更為「登入」

您可以在這裡找到完成的應用程式最終版本:https://github.com/googlecodelabs/android-kotlin-login

在本程式碼研究室中,您已瞭解以下內容:

  • 瞭解如何在 Gradle 檔案中新增必要依附元件,並在 Firebase 主控台中設定專案,將 Firebase 新增至專案。
  • 如何使用 FirebaseUI 程式庫為應用程式導入登入功能,並指定允許使用者登入的方式。請注意,使用者在您應用程式中建立的任何帳戶,都只適用於該應用程式,不會與使用 Firebase 登入功能的所有應用程式共用。
  • 如何使用 LiveData 觀察應用程式目前的驗證狀態。
  • 如何登出使用者。

本程式碼研究室介紹了如何為 Android 應用程式提供登入支援的基本概念。

在本程式碼研究室中,您允許使用者以電子郵件地址註冊及登入。不過,您也可以使用 FirebaseUI 程式庫支援其他驗證方法,例如透過電話號碼登入。如要進一步瞭解 FirebaseUI 程式庫的功能,以及如何運用程式庫提供的其他功能,請參閱下列資源:

如要進一步瞭解登入相關最佳做法,請參閱下列資源:

Codelabs:

Android 開發人員說明文件:

影片:

如要查看本課程其他程式碼研究室的連結,請參閱 Android Kotlin 進階功能程式碼研究室登陸頁面。