有关 Android 架构(视图)的建议

概念和 Jetpack Compose 实现

本页介绍了一些与架构有关的最佳实践和建议。采用这些最佳实践和建议不仅可以提高应用的质量、稳健性和可伸缩性,还可以让您的应用更便于维护和测试。

界面层

界面层的作用是在屏幕上显示应用数据,并充当主要的用户互动点。以下是一些有关界面层的最佳实践:

  • 您应该创建代码库,即使它们只包含一个数据源也不例外。
  • 在小型应用中,您可以选择将数据层类型放置在 data 软件包或模块中。

建议

说明

遵循单向数据流 (UDF) 原则。

强烈建议

遵循单向数据流 (UDF) 原则,即 ViewModel 使用观察者模式来公开界面状态,并通过方法调用接收来自界面的操作。

如果 AAC ViewModel 的优势适用于您的应用,请加以使用。

强烈建议

使用 AAC ViewModel 处理业务逻辑,并提取应用数据以向界面公开界面状态。

如需详细了解有关 ViewModel 的最佳实践,请访问此处

如需了解 ViewModel 的优势,请访问此处

使用生命周期感知型界面状态收集方式。

强烈建议

使用适当的生命周期感知型协程构建器 repeatOnLifecycle 从界面收集界面状态。

详细了解 repeatOnLifecycle

请勿将来自 ViewModel 的事件发送到界面。

强烈建议

在 ViewModel 中立即处理事件,并通过事件的处理结果引发状态更新。如需详细了解界面事件,请访问此处

使用单 activity 应用。

推荐

如果您的应用包含多个屏幕,请使用 Navigation Fragments 在屏幕之间导航以及深层链接到您的应用。

以下代码段简要说明了如何以生命周期感知型方式收集界面状态:

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 负责提供界面状态和对数据层的访问权限。以下是一些有关 ViewModel 的最佳实践:

建议

说明

ViewModel 应该与 Android 生命周期无关。

强烈建议

ViewModel 不应存储对任何与生命周期相关的类型的引用。请勿将 ActivityFragmentContextResources 作为依赖项传递。如果某元素需要在 ViewModel 中使用 Context,您应该严格评估其是否位于正确的层中。

使用协程和数据流

强烈建议

ViewModel 通过以下方式与数据层或网域层交互:

  • 通过 Kotlin 数据流接收应用数据;
  • 通过 suspend 函数使用 viewModelScope 执行操作。

在屏幕级别使用 ViewModel。

强烈建议

请勿在可重复使用的界面部分中使用 ViewModel。您应该在以下位置使用 ViewModel:

请勿使用 AndroidViewModel

强烈建议

使用 ViewModel 类,而非 AndroidViewModel。不应在 ViewModel 中使用 Application 类。正确做法是将依赖项移至界面层或数据层。

公开界面状态。

推荐

ViewModel 应该通过名为 uiState 的单个属性向界面公开数据。如果界面显示多块不相关的数据,ViewModel 可能会公开多个界面状态属性

  • 您应该将 uiState 设置为 StateFlow
  • 如果数据作为来自层次结构中的其他层的数据流传入,您应该使用 stateIn 运算符和 WhileSubscribed(5000) 政策(示例)来创建 uiState
  • 如果没有来自数据层的数据流,则属于较简单的情况,可以使用作为不可变的 StateFlow 公开的 MutableStateFlow
  • 您可以选择将 ${Screen}UiState 作为能够包含数据、错误和加载信号的数据类。如果不同状态是互斥的,该类也可以是密封的类。

以下代码段简要说明了如何从 ViewModel 公开界面状态:

@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 生命周期的最佳实践:

建议

说明

请勿替换 activity 或 fragment 中的生命周期方法。

强烈建议

请勿替换 activity 或 fragment 中的 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) {
                // ...
            }
        }
    }
}