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

1. 簡介

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

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

在本程式碼研究室中,您將建構名為「Make It So」的 Android 應用程式。這個應用程式的 UI 完全以 Jetpack Compose 建構而成。Jetpack Compose 是 Android 的新式工具包,用於建構原生 UI,不僅直覺易用,而且與編寫 .xml 檔案並將其繫結至活動、片段或檢視區塊相比,所需的程式碼更少。

如要瞭解 Firebase 和 Jetpack Compose 的搭配運作效果,第一步是瞭解新式 Android 架構。良好的架構可清楚說明元件的組織方式和彼此間的通訊方式,因此有助於瞭解、開發及維護系統。在 Android 世界中,建議的架構稱為「模型 - 檢視畫面 - ViewModel」模型代表應用程式中存取資料的層。View 是 UI 層,不應瞭解任何商業邏輯。ViewModel 會套用商業邏輯,有時需要呼叫 Model 層。

強烈建議您閱讀這篇文章,瞭解如何將 Model - View - ViewModel 套用至以 Jetpack Compose 建構的 Android 應用程式,因為這樣有助於瞭解程式碼集,並輕鬆完成後續步驟。

建構項目

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

將其設為「新增工作」畫面 設為「So Home」主畫面

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

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

課程內容

  • 如何在現代 Android 應用程式中使用 Firebase 驗證、效能監控、遠端設定和 Cloud Firestore
  • 如何讓 Firebase API 配合 MVVM 架構運作
  • 如何在 Compose UI 中反映透過 Firebase API 進行的變更

軟硬體需求

2. 取得範例應用程式並設定 Firebase

取得範例應用程式的程式碼

從指令列複製 GitHub 存放區:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

建立 Firebase 專案

  1. 使用 Google 帳戶登入 Firebase 控制台
  2. 按一下按鈕建立新專案,然後輸入專案名稱 (例如 Compose Firebase codelab)。
  3. 按一下「繼續」
  4. 如果系統提示,請詳閱並接受 Firebase 條款,然後按一下「繼續」
  5. (選用) 在 Firebase 控制台中啟用 AI 輔助功能 (稱為「Gemini in Firebase」)。
  6. 在本程式碼研究室中,您需要使用 Google Analytics 搭配遠端設定,才能使用進階指定選項,因此請將 Google Analytics 選項的切換鈕保持開啟。按照畫面上的指示設定 Google Analytics。
  7. 按一下「建立專案」,等待專案佈建完成,然後按一下「繼續」

將 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,請點選「下一步」跳至「後續步驟」
  5. 按一下「前往主控台」即可完成。

如要讓「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. 選取資料庫位置,然後按一下「下一步」
    如果是實際應用程式,請選擇離使用者較近的位置。
  5. 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
    在本節的後續步驟中,您將新增安全性規則,確保資料安全無虞。請勿在未為資料庫新增安全性規則的情況下,公開發布或公開應用程式。
  6. 點選「建立」

現在,我們來為 Firestore 資料庫建立完善的安全規則。

  1. 開啟 Firestore 資訊主頁,然後前往「規則」分頁。
  2. 將安全性規則更新為下列內容:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /tasks/{document} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null
        && resource.data.userId == request.auth.uid
        && request.data.userId == resource.data.userId;
    }
  }
}

這些規則基本上是指,應用程式的任何登入使用者都可以在任何集合中為自己建立文件。建立後,只有建立該文件的使用者可以查看、更新或刪除該文件。

執行應用程式

現在可以執行應用程式了!在 Android Studio 中開啟 make-it-so-android/start 資料夾,然後執行應用程式 (可以使用 Android 模擬器或實際的 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 再次開啟後,請務必確認「登入」和「建立帳戶」選項已消失,因為使用者現在已通過驗證。為此,請讓 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,您會將監聽器新增至 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

現在,您需要讓 Flow 中的 TasksViewModel.kt 與服務中的相同:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最後,您要讓代表 UI 的 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 提供自訂追蹤記錄。請按照下列步驟新增自訂追蹤記錄,並評估「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 或應用程式行為,這會造成不良的使用者體驗!

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

screens/tasks/TasksViewModel.kt

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

您正在擷取第一行的值,並用來載入第二行工作項目選單選項。如果值為 false,表示選單不會包含編輯選項。現在您已取得選項清單,接下來需要讓 UI 正確顯示清單。使用 Jetpack Compose 建構應用程式時,您需要尋找 composable function,該 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 驗證、效能監控、遠端設定和 Cloud Firestore 新增至完全以 Jetpack Compose 建構 UI 的 Android 應用程式,並讓應用程式符合建議的 MVVM 架構!

延伸閱讀

參考文件