本页面介绍了多项 架构 最佳实践和建议。采纳这些实践有助于提高应用的质量、稳健性和可伸缩性,并使其更易于维护和测试。
下面的最佳实践按主题分组。每项都具有一个优先级,反映了团队的推荐程度。优先级列表如下:
- 强烈建议: 您应实现此做法,除非它与您的方法从根本上发生冲突。
- 建议: 此做法可能会改进您的应用。
- 可选: 在某些情况下,此做法可以改进您的应用。
分层架构
我们建议的分层架构推崇关注点分离。它通过数据模型驱动界面,遵循单一可信来源原则,并遵守单向数据流原则。以下是分层架构的一些最佳实践:
建议 | 描述 |
---|---|
使用明确定义数据层。 强烈建议 |
数据层将应用数据暴露给应用的其余部分,并包含应用的绝大部分业务逻辑。
|
使用明确定义的界面层。 强烈建议 |
界面层在屏幕上显示应用数据,并作为用户交互的主要入口点。
|
数据层应使用数据仓库公开应用数据。 强烈建议 |
界面层中的组件(例如可组合项、Activity 或 ViewModel)不应直接与数据源交互。数据源的示例如下:
|
使用协程和流。 强烈建议 |
使用协程和流在层之间进行通信。 |
使用领域层。 建议在大型应用中使用 |
如果您需要在多个 ViewModel 中重用与数据层交互的业务逻辑,或者您想简化特定 ViewModel 的业务逻辑复杂性,请使用领域层和用例。 |
界面层
界面层的作用是在屏幕上显示应用数据并作为用户交互的主要入口点。以下是界面层的一些最佳实践:
建议 | 描述 |
---|---|
遵循单向数据流 (UDF)。 强烈建议 |
遵循单向数据流 (UDF)原则,其中 ViewModel 使用观察者模式公开界面状态,并通过方法调用从界面接收操作。 |
如果AAC ViewModel的优势适用于您的应用,请使用它们。 强烈建议 |
使用AAC ViewModel来处理业务逻辑,并获取应用数据以将界面状态暴露给界面(Compose 或 Android 视图)。 |
使用生命周期感知型界面状态收集。 强烈建议 |
使用适当的生命周期感知型协程构建器从界面收集界面状态:视图系统中使用 repeatOnLifecycle ,Jetpack Compose 中使用 collectAsStateWithLifecycle 。阅读有关 阅读有关 |
不要将事件从 ViewModel 发送到界面。 强烈建议 |
立即在 ViewModel 中处理事件,并通过事件处理结果触发状态更新。有关界面事件的更多信息请见此处。 |
使用单 Activity 应用。 建议 |
如果您的应用有多个屏幕,请使用Navigation Fragment或Navigation Compose在屏幕之间导航,并为您的应用设置深层链接。 |
使用Jetpack Compose。 建议 |
使用Jetpack Compose为手机、平板电脑、可折叠设备和 Wear OS 构建新应用。 |
以下代码段概述了如何以生命周期感知的方式收集界面状态:
视图
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负责提供界面状态和数据层访问权限。以下是 ViewModel 的一些最佳实践:
建议 | 描述 |
---|---|
ViewModel 应与 Android 生命周期无关。 强烈建议 |
ViewModel 不应持有任何与生命周期相关的类型的引用。不要将 Activity, Fragment, Context 或 Resources 作为依赖项传递。如果 ViewModel 中需要 Context ,您应该认真评估其是否位于正确的层。 |
使用协程和流。 强烈建议 |
ViewModel 使用以下方式与数据层或领域层交互:
|
在屏幕级别使用 ViewModel。 强烈建议 |
不要在可重用的界面片段中使用 ViewModel。您应该在以下场景中使用 ViewModel:
|
在可重用界面组件中使用普通状态持有者类。 强烈建议 |
使用普通状态持有者类来处理可重用界面组件中的复杂性。通过这样做,状态可以被提升并在外部控制。 |
不要使用 AndroidViewModel 。建议 |
使用 ViewModel 类,而不是 AndroidViewModel 。Application 类不应在 ViewModel 中使用。相反,将依赖项移动到界面层或数据层。 |
暴露界面状态。 建议 |
ViewModel 应通过名为 uiState 的单个属性向界面公开数据。如果界面显示多个不相关的数据片段,VM 可以公开多个界面状态属性。
|
以下代码段概述了如何从 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) {
// ...
}
}
}
}
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 。 |