使用 Firebase 和 Jetpack Compose 建構 Android 應用程式

1. 簡介

上次更新時間:2022 年 11 月 16 日

使用 Firebase 和 Jetpack Compose 建構 Android 應用程式

在本程式碼研究室中,您將建構名為 Make It So 的 Android 應用程式。此應用程式的 UI 完全採用 Jetpack Compose 建構,這是 Android 用於建構原生 UI 的新型工具包,使用起來直覺且所需程式碼比撰寫 .xml 檔案並將其繫結至活動、Fragment 或 View 少。

如要瞭解 Firebase 和 Jetpack Compose 的搭配運作情形,第一步是瞭解現代 Android 架構。良好的架構可讓系統易於理解、開發及維護,因為它會清楚說明元件的組織方式和彼此間的溝通方式。在 Android 世界中,建議的架構稱為 Model - View - ViewModelModel 代表應用程式中存取資料的層。View 是 UI 層,不應知道任何商業邏輯。而 ViewModel 是套用商業邏輯的地方,有時需要 ViewModel 呼叫 Model 層。

強烈建議您參閱本文,瞭解如何將 Model - View - ViewModel 套用至使用 Jetpack Compose 建構的 Android 應用程式,因為這有助於您更容易瞭解程式碼集,並更輕鬆地完成後續步驟。

建構項目

Make It So 是一款簡單的待辦事項清單應用程式,可讓使用者新增及編輯工作、新增標記、優先順序和截止日期,以及將工作標示為已完成。下方圖片顯示此應用程式的兩個主要頁面:工作建立頁面和顯示已建立工作清單的主頁面。

Make it So 新增工作畫面 Make it So 主畫面

您將新增此應用程式缺少的部分功能:

  • 使用電子郵件地址和密碼驗證使用者
  • 在 Firestore 集合中新增監聽器,讓 UI 對變更做出反應
  • 新增自訂追蹤記錄,監控應用程式中特定程式碼的效能
  • 使用遠端設定建立功能切換鈕,並透過階段性推出作業發布

課程內容

  • 如何在現代 Android 應用程式中使用 Firebase 驗證、Performance Monitoring、遠端設定和 Cloud Firestore
  • 如何讓 Firebase API 符合 MVVM 架構
  • 如何在 Compose UI 中反映使用 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、網頁、Flutter 和 Unity。

請選擇 Android 選項,如下所示:

Firebase 專案總覽

然後按照下列步驟操作:

  1. 輸入 com.example.makeitso 做為套件名稱,並視需要輸入暱稱。在本程式碼研究室中,您不需要新增偵錯簽署憑證。
  2. 點選「下一步」即可註冊應用程式,並存取 Firebase 設定檔。
  3. 按一下「Download google-services.json」,下載設定檔並儲存在 make-it-so-android/app 目錄中。
  4. 點選「下一步」。由於範例專案的 build.gradle 檔案中已包含 Firebase SDK,請點選「Next」跳至「Next steps」
  5. 按一下「Continue to console」即可完成。

如要讓 Make it So 應用程式正常運作,請在跳轉至程式碼前,先在控制台中執行兩項操作:啟用驗證服務供應器,並建立 Firestore 資料庫。

設定驗證

首先,我們要啟用驗證功能,讓使用者能夠登入應用程式:

  1. 在「Build」選單中,依序選取「Authentication」和「Get Started」
  2. 在「登入方式」資訊卡中,選取「電子郵件/密碼」並啟用。
  3. 接著,按一下「新增供應者」,然後選取並啟用「匿名」

設定 Cloud Firestore

接著,請設定 Firestore。您將使用 Firestore 儲存已登入使用者的任務。每位使用者都會在資料庫的集合中取得自己的文件

  1. 在 Firebase 主控台的左側面板中展開「Build」,然後選取「Firestore database」
  2. 按一下 [Create database] (建立資料庫)。
  3. 將「資料庫 ID」設為 (default)
  4. 選取資料庫的位置,然後按一下「Next」
    如果是實際應用程式,請選擇距離使用者較近的位置。
  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 Emulator 或實際的 Android 裝置執行)。

3. Firebase 驗證

您要新增哪項功能?

Make It So 範例應用程式的目前狀態下,使用者可以開始使用應用程式,無須先登入。這項功能會使用匿名驗證來達成此目標。不過,匿名帳戶無法讓使用者存取其他裝置上的資料,甚至無法在日後的工作階段存取。雖然匿名驗證功能可用於溫暖式新手上路流程,但您應一律提供使用者轉換為其他登入表單的選項。考量到這一點,您將在本程式碼研究室中,為 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 控制台的驗證資訊主頁中建立的新帳戶,請按一下「使用者」分頁。

4. Cloud Firestore

您要新增哪項功能?

針對 Cloud Firestore,您將在 Firestore 集合中新增事件監聽器,該集合會儲存代表 Make it So 中顯示的任務的文件。新增此事件監聽器後,您就會收到對此集合所做的每項更新。

該寫程式碼了!

更新 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 與服務中的 Flow 一致:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最後,請讓 TasksScreens.kt 中的 composable function (代表 UI) 注意這個流程,並將其收集為狀態。每次狀態變更時,可組合函式都會自動重新組合,並向使用者顯示最新狀態。將以下內容新增至 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 效能監控提供自訂追蹤記錄。請按照下列步驟新增自訂追蹤,並在「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)

您可以選擇要評估的程式碼庫部分,並為其新增自訂追蹤記錄。以下是將自訂追蹤記錄新增至您先前在本程式碼研究室中看到的 linkAccount 函式 (AccountServiceImpl.kt) 的範例:

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 控制台並前往效能資訊主頁。畫面底部會顯示三個分頁:「網路要求」、「自訂追蹤記錄」和「螢幕算繪」

前往「Custom traces」分頁,確認程式碼庫中新增的追蹤記錄是否顯示在該分頁中,並查看執行這些程式碼通常需要多長時間。

6. 遠端設定

您要新增哪項功能?

遠端設定的用途多元,從遠端變更應用程式外觀,到為不同使用者區隔設定不同行為皆可。在本程式碼研究室中,您將使用 Remote Config 建立功能切換鈕,在 Make it So 應用程式中顯示或隱藏新的「編輯工作」功能。

該寫程式碼了!

首先,請在 Firebase 主控台中建立設定。如要這樣做,請前往 遠端設定資訊主頁,然後點選「新增參數」按鈕。按照下圖填寫欄位:

遠端設定「建立參數」對話方塊

填妥所有欄位後,請按一下「儲存」按鈕,然後點選「發布」。參數已建立並可供程式碼集使用,因此您需要新增程式碼,以便在應用程式中擷取新值。請開啟 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 中)。這是確保所有畫面一開始就能顯示最新值的最佳方法。如果您在使用者執行某項操作時,稍後變更應用程式的 UI 或行為,這會導致使用者體驗不佳!

第二個函式會傳回針對您剛在控制台中建立的參數發布的布林值。您需要在 loadTaskOptions 函式中加入下列內容,才能在 TasksViewModel.kt 中擷取這項資訊:

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

您會擷取第一行中的值,並使用該值載入第二行中工作項目的選單選項。如果值為 false,表示選單中不會包含編輯選項。您現在已取得選項清單,因此需要讓 UI 正確顯示選項。使用 Jetpack Compose 建構應用程式時,您需要尋找 composable function,宣告 TasksScreen 的 UI 應有的外觀。因此,請開啟 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,用於宣告單一工作項的 UI 應有的外觀。每個工作都有選單,當使用者按一下工作結尾處的三點圖示,就會顯示選項。

測試時間!

您現在可以執行應用程式了!確認您透過 Firebase 控制台發布的值與應用程式行為相符:

  • 如果是 false,點選三點圖示時,您應該只會看到兩個選項。
  • 如果是 true,點選三點圖示後,您應該會看到三個選項:

請嘗試在控制台中變更幾次值,然後重新啟動應用程式。透過遠端設定,您就能輕鬆在應用程式中推出新功能!

7. 恭喜!

恭喜,您已成功使用 Firebase 和 Jetpack Compose 建構 Android 應用程式!

您已將 Firebase 驗證、Performance Monitoring、遠端設定和 Cloud Firestore 新增至完全使用 Jetpack Compose 建構的 Android 應用程式 UI,並將其納入建議的 MVVM 架構!

延伸閱讀

參考文件