上期回顾:深入理解 Android 架构 Clean Architecture(介绍篇)
首先代码将会独立分为三层:
Data Layer(数据层)是三层之中最底层,它包含应用数据和业务逻辑。业务逻辑决定应用的价值,它包含决定应用如何创建、存储和更改应用数据。
Data Layer(数据层)由一个或多个 Repository(存储库 )组成,其中每个存储库可包含一个或多个 Data Source(数据源)。数据源负责提供应用所需的数据,它们可能是远程API或者本地数据库。
Repository 在数据层中承担了抽象数据访问、集中管理数据来源以及简化数据访问的作用,为业务逻辑提供了清晰且统一的数据访问接口。
它是应用程序与外部数据之间的桥梁,负责管理数据的存储、获取和传输,并通过 Repository 等抽象层向其他部分提供统一的数据访问接口。
总结:Data Layer(数据层)负责存储、管理以及提供应用数据。
Domain Layer(领域层)位于 Data Layer(数据层)和 Presentation(表示层)之间,它可以简化应用架构、易于理解,具有扩展和易于测试。
Domain Layer(领域层)通常由 Use Case 和领域模型组成,负责处理应用程序的业务逻辑和核心功能。业务逻辑与UI逻辑不同,UI逻辑定义如何在屏幕上显示内容,而业务逻辑定义如何处理事件和数据更改。
在小型应用上,业务逻辑通常在 ViewModel 或者数据层中进行,但是随着应用功能不断地发展,ViewModel会变得越来越重,因此将所有业务逻辑合并到领域层是非常有意义的。
Domain Layer(领域层)不会负责数据的显示,因为这是表现层的工作,也不负责获取和存储数据,这是数据层的工作。
命名:功能名 + UsuCase
作用:封装可复用的业务逻辑、组合多个存储库的数据
示例代码:LoginUseCase 负责处理用户登录的业务逻辑。在登录之前进行一系列的邮箱、密码格式验证后调用下层的 repository 执行登录操作,最后表示层在用户触发登录操作后调用该 Use Case。
class LoginUseCase(
private val repository: AuthRepository,
) {
suspend operator fun invoke(
email: String,
password: String
): LoginResult {
//在这里处理业务逻辑
val emailError = ValidationUtil.validateEmail(email)
val passwordError = if (password.isBlank()) AuthError.FieldEmpty else null
if (emailError != null || passwordError != null) {
return LoginResult(
emailError = emailError, passwordError = passwordError
)
}
return LoginResult(
result = repository.login(email.trim(), password.trim())
)
}
}
因为 Use Case 从始至终都专注于处理业务一件事,所以它可以使用 Kotlin 中的调用运算符从而在 ViewModel 中像普通函数调用它即可。这样做也符合 Clean Architecture 中分离关注点、单一职责的原则,提高复用性和测试性,清晰的代码结构帮助开发者后期维护。
@HiltViewModel
class LoginViewModel @Inject constructor(
private val loginUseCase: LoginUseCase,
) : ViewModel() {
...
private fun login() {
viewModelScope.launch {
...
val loginError = loginUseCase(
email = emailState.value.text,
password = passwordState.value.text
)
...
}
}
}
在领域层,通常会定义领域模型,即业务对象和实体。一般数据源返回的模型可能不是其他层所完全需要的模型,通俗点来讲服务器返回了一篇文章的所有信息例如:编号、内容、标题、时间、类型、作者等等,但是我们只需要编号标题和内容,其它数据不需要,所有需要领域层定义对应的领域模型。
示例代码:
/** 数据层的模型 */
data class ArticleModel(
val id:Int,
val title:String,
val content:String,
val timestamp:Long,
val author:String,
val type:String
){
// 映射成领域层的模型
fun toArticle():Article{
return Article(
id = id,
title = title,
content = content
)
}
}
/** 领域层的模型 */
data class Article(
val id:Int,
val title:String,
val content:String,
)
这不仅使代码更加整洁,还能更好地隔离潜在的问题,使每一层定义自己所需的模型。
Domain Layer(领域层)负责应用程序的核心业务逻辑
Presentation Layer(表示层)位于三层的最外层,它负责处理与用户界面(UI)交互相关的事务。
Presentation Layer(表示层)在官方说明文档中是由UI组件和状态持有者组成,但是我个人觉得划分为三部分会更加清晰,分别为UI组件、UI的状态以及 ViewModel 三部分组成。
UI Elements 一般是指用户界面中的元素,负责向用户展示信息、接收输入或执行特定的交互功能。例如在 Compose 中的 Screen 或者某一个组件,也可以是View系统中的Activity、Fragment。
UI State 是指用户界面的当前状态或条件,其中包含了影响用户界面显示和行为的各种数据。UI State 大到可以作为一个页面的状态,小到为一个组件的状态。
示例代码:
// 关于用户列表的状态
data class PersonListState(
val isLoading: Boolean = false
val users: List<UserItem> = emptyList(),
)
ViewModel 主要负责处理用户与界面交互的UI逻辑,在表示层中是扮演着状态持有者的角色,它会存储并公开界面要使用的状态,界面状态是经过 ViewModel 转换的应用数据。
首先 UI 会引用 ViewModel 公开的状态,随即界面向 ViewModel 发送用户的事件,然后 ViewModel 进行一系列的处理后更新状态,状态一旦更改就会驱动UI进行刷新,最后呈现给用户,这就形成了单向的数据流也是MVI设计模式的核心。
示例代码:
@Composable
fun PersonListScreen(
viewModel: PersonListViewModel = hiltViewModel()
) {
val state = viewModel.state.value
Column(
modifier = Modifier.fillMaxSize()
) {
// 发起UI事件
Button(onClick = {
viewModel.getPersonList(id)
}) {
Text(text = "获取数据")
}
LazyColumn(
modifier = Modifier.fillMaxSize()
){
items(state.users) { ... }
}
}
}
@HiltViewModel
class PersonListViewModel @Inject constructor(
...
) : ViewModel() {
// --- 状态 ---
private val _uiState = mutableStateOf(PersonListState())
val uiState: State<PersonListState> = _uiState
// --- 这里统一处理用户事件 ---
fun onEvent(event:PersonEvent){
when(event){
...
is PersonEvent.GetData -> getPersonList()
}
}
private fun getPersonList() {
viewModelScope.launch {
//在获取用户列表时会更新状态为加载中
_state.value = state.value.copy(isLoading = true)
//获取完成后根据结果更新状态
when (val result = personUseCases.getPersonList()) {
is Resource.Error -> {
//加载失败发出加载失败的UI事件
...
_state.value = state.value.copy(isLoading = false)
}
is Resource.Success -> {
//加载成功后更新状态的值
_state.value = state.value.copy(
users = result.data ?: emptyList(),
isLoading = false
)
}
}
}
}
}
Presentation Layer(表现层)负责处理用户界面的显示逻辑以及协调用户交互操作呈现数据。
最后,可以关注一下本人的公众号,会不定期发布一些关于 Android、Kotlin、Jetpack Compose相关的学习笔记和知识。