Android 架構建議 (檢視區塊)

概念和 Jetpack Compose 實作

本頁提供多個架構的最佳做法和建議。採用這些最佳做法和建議,可提升應用程式的品質、穩定性和擴充性,還能更輕鬆地維護及測試應用程式。

UI 層

UI 層的作用是在螢幕上顯示應用程式資料,且為使用者與應用程式互動的主要管道。以下是 UI 層的最佳做法:

  • 即使只有單一資料來源,仍應建立存放區
  • 在小型應用程式中,可以選擇將資料層類型放入 data 套件或模組。

建議

說明

遵循單向資料流 (UDF)

強烈建議

遵循單向資料流程 (UDF) 原則,其中 ViewModel 使用觀測器模式公開 UI 狀態,並透過方法呼叫從 UI 接收動作。

如果 AAC ViewModel 對您的應用程式有幫助,建議多加利用。

強烈建議

使用 AAC ViewModel 處理商業邏輯,並擷取應用程式資料,以便在 UI 中公開 UI 狀態。

請參閱這裡的 ViewModel 最佳做法瞭解詳情。

請參閱這裡的 ViewModel 優點

使用生命週期感知方法收集 UI 狀態。

強烈建議

使用適合的生命週期感知協同程式建構工具 repeatOnLifecycle,從 UI 中收集 UI 狀態。

進一步瞭解 repeatOnLifecycle

請勿將事件從 ViewModel 傳送至 UI。

強烈建議

在 ViewModel 中立即處理事件,並引起狀態更新,進而處理事件。請參閱這裡的 UI 事件瞭解詳情。

使用單一活動應用程式。

推薦項目

如果應用程式有多個畫面,請使用導覽片段前往不同畫面,以及深層連結至應用程式。

下列文字片段說明如何以生命週期感知方式收集 UI 狀態:

class MyFragment : Fragment() {

    private val viewModel: MyViewModel by viewModel()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Process item
                }
            }
        }
    }
}

ViewModel

ViewModel 負責提供 UI 狀態和資料層存取權。以下是一些 ViewModel 最佳做法:

建議

說明

ViewModel 應不受 Android 生命週期的影響。

強烈建議

ViewModel 不應保留任何生命週期相關類型的參照。請勿將 ActivityFragmentContextResources 做為依附元件傳遞。如果 ViewModel 中有任何項目需要 Context,您應密切評估項目是否位於正確的層。

使用協同程式和資料流

強烈建議

ViewModel 透過以下方式與資料或網域層互動:

  • Kotlin 資料流,用於接收應用程式資料。
  • suspend 函式,使用 viewModelScope 執行動作。

在畫面層級使用 ViewModel。

強烈建議

不要在可重複使用的 UI 中使用 ViewModel,您應在以下位置使用 ViewModel:

  • 檢視畫面中的活動/片段
  • 使用 Jetpack Navigation 時的到達網頁或圖表。

請勿使用 AndroidViewModel

強烈建議

使用 ViewModel 類別,而非 AndroidViewModel。不應在 ViewModel 中使用 Application 類別,請改為將依附元件移至 UI 或資料層。

公開 UI 狀態。

推薦項目

ViewModel 應透過名為 uiState 的單一屬性,向 UI 公開資料。如果 UI 顯示多個不相關的資料,ViewModel 可能會公開多個 UI 狀態屬性

  • 您應將 uiState 設為 StateFlow
  • 您應使用 stateIn 運算子搭配 WhileSubscribed(5000) 政策 (範例),建立 uiState 做為階層中其他層的資料串流。
  • 如果案例較簡單,沒有任何資料串流來自資料層,則可以使用 MutableStateFlow 做為不可變更的 StateFlow 公開。
  • 您可以選擇將 ${Screen}UiState 設為資料類別,其中可包含資料、錯誤和載入信號。如果排除不同的狀態,這個類別也可以是密封類別。

以下文字片段概述了如何從 ViewModel 公開 UI 狀態:

@HiltViewModel
class BookmarksViewModel @Inject constructor(
    newsRepository: NewsRepository
) : ViewModel() {

    val feedState: StateFlow<NewsFeedUiState> =
        newsRepository
            .getNewsResourcesStream()
            .mapToFeedState(savedNewsResourcesState)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = NewsFeedUiState.Loading
            )

    // ...
}

生命週期

以下是使用 Android 生命週期的一些最佳做法:

建議

說明

不要覆寫活動或片段中的生命週期方法。

強烈建議

不要覆寫活動或片段中的生命週期方法,例如 onResume。請改用 LifecycleObserver。如果應用程式在生命週期達到特定 Lifecycle.State 時需要執行作業,請使用 repeatOnLifecycle API。

下列文字片段概述了如何根據特定生命週期狀態來執行作業:

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                // ...
            }
            override fun onPause(owner: LifecycleOwner) {
                // ...
            }
        }
    }
}