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

1. 簡介

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

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

在本程式碼研究室中,您會建構一款名為「Make It So」的 Android 應用程式。這個應用程式的 UI 完全是以 Jetpack Compose 建構而成。Jetpack Compose 是 Android 的新型工具包,可用來建構原生 UI。相較於編寫 .xml 檔案,並將其繫結至活動、片段或 View,那麼操作直覺且只需較少的程式碼。

瞭解 Firebase 和 Jetpack Compose 如何搭配運作的第一步,就是瞭解現代 Android 架構。良好的架構能讓系統輕鬆理解、輕鬆開發和維護,因為這樣系統就能清楚瞭解元件的組織方式,以及彼此之間的通訊。在 Android 環境中,建議的架構稱為「Model - View - ViewModel」。「模型」代表在應用程式中存取資料的層。View 是 UI 層,不應瞭解商業邏輯。而 ViewModel 則是商業邏輯套用的位置,有時需要 ViewModel 才能呼叫 Model 層。

強烈建議您參閱這篇文章,瞭解如何將「模型 - View - ViewModel」套用至使用 Jetpack Compose 建構的 Android 應用程式,這樣程式碼集將更容易理解,也有助於完成後續步驟。

建構項目

「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 控制台

按照畫面上的步驟完成專案建立作業。

您可以在每項 Firebase 專案中建立不同的應用程式:Android、iOS、Web、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. 按一下「Continue to console」即可完成。

為確保 Make It So 應用程式正常運作,在跳到程式碼之前,您必須在控制台中完成兩項操作:啟用驗證服務供應商並建立 Firestore 資料庫。首先,請啟用驗證功能,讓使用者能夠登入應用程式:

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

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

  1. 在「Build」選單中,選取「Firestore」,然後按一下「Create database」
  2. 保持啟用「在正式環境中啟動」,然後點選「下一步」
  3. 畫面出現提示時,請選取 Cloud Firestore 資料的儲存位置。開發正式版應用程式時,可能需要位於大多數使用者較近的地區,也應與其他 Firebase 服務 (例如「函式」) 保持相同。在本程式碼研究室中,您可以保留預設區域或選取距離您最近的區域。
  4. 按一下「啟用」來佈建 Firestore 資料庫。

請花點時間為 Firestore 資料庫建立完善的安全性規則。開啟 Firestore 資訊主頁,前往「規則」分頁。接著更新安全性規則,如下所示:

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 函式:

畫面/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

首先,它會嘗試進行驗證,如果呼叫成功,就會前往下一個畫面 (SettingsScreen)。當您在 launchCatching 區塊內執行這些呼叫時,如果第一行發生錯誤,會擷取並處理例外狀況,甚至不會達到第二行。

當使用者再次開啟 SettingsScreen 時,您必須確認「登入」和「建立帳戶」的選項已經刪除,因為使用者現已通過驗證。若要這麼做,我們要讓 SettingsViewModel 監聽目前使用者的狀態 (適用於 AccountService.kt),檢查帳戶是否匿名。如要這樣做,請將 SettingsViewModel.kt 中的 uiState 更新為如下所示:

畫面/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

最後,您需要更新 SettingsScreen.kt 中的 uiState,收集 SettingsViewModel 發出的狀態:

畫面/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

現在,每當使用者變更時,SettingsScreen 都會重新組合,根據新的驗證狀態顯示選項。

現在就測試!

執行 Make it So,然後按一下畫面右上角的齒輪圖示前往設定。接著,按一下建立帳戶的選項:

設為設定畫面 設為註冊畫面

輸入有效的電子郵件和安全強度高的密碼,以建立帳戶。應該可正常運作,系統會將你重新導向至設定頁面,當中會顯示兩個新選項:登出並刪除帳戶。如要查看新建立的帳戶,請前往 Firebase 控制台的驗證資訊主頁,然後點選「使用者」分頁標籤。

4. Cloud Firestore

請問你想新增哪項功能?

針對 Cloud Firestore,請將事件監聽器新增至 Firestore 集合,其中儲存代表顯示工作的文件。新增這個事件監聽器後,您將收到對此集合所做的每個更新。

該寫程式了!

更新 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 反映服務中的相同:

畫面/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最後,我們要在 TasksScreens.kt 中製作代表 UI 的 composable function,請注意這個資料流並收集為狀態。每當狀態變更,可組合函式就會自動重組,並向使用者顯示最新的狀態。將此內容新增至 TasksScreen composable function

畫面/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

當可組合函式具備這些狀態的存取權後,您可以更新 LazyColumn (用於在畫面中顯示清單的結構),如下所示:

畫面/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

現在就測試!

如要測試是否正常運作,請使用應用程式新增工作 (按一下畫面右下角的「新增」按鈕)。工作建立完成後,應會顯示在 Firestore 控制台的 Firestore 集合中。只要在其他裝置中使用相同帳戶登入,就能編輯待辦事項,並在所有裝置上即時查看這些項目的更新。

5. 監控效能

請問你想新增哪項功能?

效能是非常重要的重點,因為如果應用程式效能不佳,使用者很可能會放棄使用您的應用程式,他們花太多時間透過應用程式完成簡單的工作。因此,收集一些有關使用者在應用程式中歷程的指標是很實用的做法。為此,Firebase Performance Monitoring 提供自訂追蹤記錄。請按照後續步驟新增自訂追蹤記錄,並評估「產生如此」各程式碼片段的成效。

該寫程式了!

如果您開啟 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)

您可以選擇程式碼集的哪些部分重要評估,並加入自訂追蹤記錄。以下範例說明如何將自訂追蹤記錄新增至先前在 AccountServiceImpl.kt 中看過的 linkAccount 函式:

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()
  }

現在輪到您了!將一些自訂追蹤記錄新增至讓應用程式符合,請繼續下一節測試是否能正常運作。

現在就測試!

新增自訂追蹤記錄後,請執行應用程式,並使用要多次評估的功能。接著,前往 Firebase 控制台,前往「效能資訊主頁」。畫面底部會顯示三個分頁:「網路要求」、「自訂追蹤記錄」和「畫面轉譯」

前往「Custom traces」分頁,確認程式碼集中顯示新增的追蹤記錄,您可以查看執行這些程式碼通常需要多少時間。

6. 遠端設定

請問你想新增哪項功能?

遠端設定的用途相當多元,從遠端變更應用程式外觀,到為不同使用者區隔設定不同行為,都涵蓋在內。在本程式碼研究室中,您將使用遠端設定建立功能切換鈕,藉此在「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 函式中加入以下內容:

畫面/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 中的選項:

畫面/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 for UI 建構的 Android 應用程式,並且使其符合建議的 MVVM 架構!

其他資訊

參考說明文件