此页面介绍了一些架构最佳实践和建议。采用它们可以提高应用的质量、健壮性和可扩展性。它们还有助于简化应用的维护和测试。
以下最佳实践按主题分组。每个都具有优先级,反映了团队推荐的力度。优先级列表如下
- 强烈推荐:除非与您的方法存在根本冲突,否则您应该实施此实践。
- 推荐:此实践可能会改进您的应用。
- 可选:在某些情况下,此实践可以改进您的应用。
分层架构
我们推荐的分层架构有利于关注点分离。它通过数据模型驱动 UI,符合单一数据源原则,并遵循单向数据流原则。以下是分层架构的一些最佳实践
建议 | 描述 |
---|---|
使用明确定义的数据层。 强烈推荐 |
数据层将应用程序数据公开给应用的其余部分,并包含应用的大部分业务逻辑。
|
使用明确定义的UI 层。 强烈推荐 |
UI 层在屏幕上显示应用程序数据,并用作用户交互的主要点。
|
数据层data layer 应该使用资源库公开应用程序数据。 强烈推荐 |
UI 层中的组件,例如可组合函数、活动或 ViewModel,不应直接与数据源交互。数据源示例包括:
|
使用协程和流。 强烈推荐 |
使用协程和流在层之间进行通信。 |
使用领域层。 建议在大应用中使用 |
如果您需要跨多个 ViewModel 重用与数据层交互的业务逻辑,或者您希望简化特定 ViewModel 的业务逻辑复杂度,请使用领域层和用例。 |
UI 层
UI 层UI layer 的作用是在屏幕上显示应用程序数据,并充当用户交互的主要入口点。以下是 UI 层的一些最佳实践:
建议 | 描述 |
---|---|
遵循单向数据流 (UDF)。 强烈推荐 |
遵循单向数据流 (UDF)原则,其中 ViewModel 使用观察者模式公开 UI 状态,并通过方法调用从 UI 接收操作。 |
如果其优势适用于您的应用,请使用AAC ViewModel。 强烈推荐 |
使用AAC ViewModel来处理业务逻辑,并获取应用程序数据以向 UI(Compose 或 Android 视图)公开 UI 状态。 查看更多ViewModel 最佳实践请点击此处。 |
使用生命周期感知的 UI 状态收集。 强烈推荐 |
使用适当的生命周期感知协程构建器从 UI 中收集 UI 状态:在视图系统中使用repeatOnLifecycle ,在 Jetpack Compose 中使用collectAsStateWithLifecycle 。阅读更多关于 阅读更多关于 |
不要从 ViewModel 发送事件到 UI。 强烈推荐 |
在 ViewModel 中立即处理事件,并使用处理事件的结果导致状态更新。更多关于UI 事件请点击此处。 |
使用单活动应用程序。 推荐 |
如果您的应用有多个屏幕,请使用导航片段或导航 Compose在屏幕之间导航,并深层链接到您的应用。 |
使用Jetpack Compose。 推荐 |
使用Jetpack Compose为手机、平板电脑和折叠屏设备以及 Wear OS 构建新的应用。 |
以下代码片段概述了如何在生命周期感知的方式下收集 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
}
}
}
}
}
Compose
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModel 负责提供 UI 状态和对数据层的访问。以下是 ViewModel 的一些最佳实践:
建议 | 描述 |
---|---|
ViewModel 应该与 Android 生命周期无关。 强烈推荐 |
ViewModel 不应该持有任何与生命周期相关的类型的引用。不要将Activity、Fragment、Context 或Resources 作为依赖项传递。如果 ViewModel 中的内容需要Context ,则应认真评估它是否在正确的层级中。 |
使用协程和流。 强烈推荐 |
ViewModel 使用以下方式与数据层或领域层交互:
|
在屏幕级别使用 ViewModel。 强烈推荐 |
不要在可重用的 UI 部分中使用 ViewModel。您应该在以下情况下使用 ViewModel:
|
在可重用的 UI 组件中使用普通状态持有者类。 强烈推荐 |
在可重用的 UI 组件中使用普通状态持有者类来处理复杂性。通过这样做,可以将状态提升并由外部控制。 |
不要使用AndroidViewModel 。推荐 |
使用ViewModel 类,而不是AndroidViewModel 。Application 类不应在 ViewModel 中使用。相反,将依赖项移动到 UI 或数据层。 |
公开 UI 状态。 推荐 |
ViewModel 应该通过一个名为uiState 的单个属性向 UI 公开数据。如果 UI 显示多个不相关的部分数据,则 VM 可以公开多个 UI 状态属性。
|
以下代码片段概述了如何从 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) {
// ...
}
}
}
}
Compose
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
处理依赖项
在管理组件之间的依赖项时,您应该遵守以下一些最佳实践:
建议 | 描述 |
---|---|
使用依赖项注入。 强烈推荐 |
使用依赖项注入最佳实践,尽可能使用构造函数注入。 |
在必要时限定作用域到组件。 强烈推荐 |
当类型包含需要共享的可变数据,或者类型初始化成本高且在应用中被广泛使用时,限定作用域到依赖项容器。 |
使用Hilt。 推荐 |
在简单的应用中使用Hilt或手动依赖项注入。如果您的项目足够复杂,请使用Hilt。例如,如果您有:
|
测试
以下是一些测试的最佳实践:
建议 | 描述 |
---|---|
了解要测试的内容.
强烈推荐 |
除非项目非常简单,例如 Hello World 应用,否则您应该对其进行测试,至少应包括:
|
优先使用模拟数据而不是模拟对象。 强烈推荐 |
阅读Android 文档中使用测试替身的更多内容。 |
测试 StateFlow。 强烈推荐 |
在测试StateFlow 时
|
有关更多信息,请查看Android DAC 指南中要测试的内容。
模型
在开发应用中的模型时,您应该遵守以下最佳实践:
建议 | 描述 |
---|---|
在复杂的应用中为每个层创建模型。 推荐 |
在复杂的应用中,当有意义时,在不同的层或组件中创建新的模型。请考虑以下示例:
|
命名约定
在命名代码库时,您应该了解以下最佳实践:
建议 | 描述 |
---|---|
命名方法。 可选 |
方法应该是动词短语。例如,makePayment() 。 |
命名属性。 可选 |
属性应该是名词短语。例如,inProgressTopicSelection 。 |
命名数据流。 可选 |
当类公开 Flow 流、LiveData 或任何其他流时,命名约定为get{model}Stream() 。例如,getAuthorStream(): Flow<Author> 如果函数返回模型列表,则模型名称应为复数:getAuthorsStream(): Flow<List<Author>> |
命名接口实现。 可选 |
接口实现的名称应具有意义。如果找不到更好的名称,则使用Default 作为前缀。例如,对于NewsRepository 接口,您可以使用OfflineFirstNewsRepository 或InMemoryNewsRepository 。如果找不到合适的名称,则使用DefaultNewsRepository 。模拟实现应以Fake 作为前缀,例如FakeAuthorsRepository 。 |