Android开发系列(一)Clean Architecture示例

前言

    软件架构可以简单理解为代码的组织方式,如果把软件比作一个建筑物,那么软件架构就是建筑的主体框架。建筑一个矮小的土坯房不需要复杂的框架,同样一个简单的项目也不需要过多注重架构的设计。如果要建造一个摩天大厦,如果没有一个好的主体框架是很难建起来的,同样地要开发一个大型的软件项目,也需要进行架构的设计。

    Clean Architecture为Android官方推荐的应用架构,该架构基于关注点分离和依赖反转的原则。它将应用程序分为多个层,每个层都有特定的职责。这些层通常包括表示层(用户界面)、域层(业务逻辑)和数据层(数据访问),如下图所示。

从上图可以看出,Clean Architecture将应用程序分为3层。

界面层(UI Layer):界面层的作用是在屏幕上显示应用数据,并充当主要的用户互动点。

网域层(Domain Layer):网域名位于界面层与数据层之间,是一个可选层,主要封装负责的业务逻辑。

数据层(Data Layer):数据层为应用提供应用数据,应用数据的来源可以是云端的服务器,也可以是本地数据库。

下面通过一个简单的示例加深理解Clean Architecture分层设计思想。

UI Layer

    本示例介绍通过手机号登录的流程,UI Layer主要包含Screen,ViewModel和UiState,下面分别介绍它们的具体实现。

1)LoginScreen.kt

    LoginScreen主要绘制登录界面,以及与用户交互的逻辑,代码如下所示。

@SuppressLint("UnrememberedMutableState")
@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val loginUiState by viewModel.loginUiState.collectAsState()
    Column {
        Button(onClick = { viewModel.onLoginByPhoneSmsCodeTriggered("13811118888","1234")}) {
            Text(text = "Login")
        }
        when(loginUiState){
            LoginUiState.Login -> Text(text = "already Login")
            LoginUiState.UnLogin -> Text(text = "not Login")
        }
    }
}

     这里使用Jetpack Compose绘制界面,在下一讲开始介绍Jetpack Compose相关UI组件的用法。这里用到了3个UI组件,Bottun是一个按钮,Text用于显示文字,Column用于布局子UI控件。

    这里会根据登录的状态显示already Login还是not Login,刚开始处于为登录状态,所以显示“not Login”,登录界面显示如下。

2)LoginViewModel.kt

    用于点击登录界面的按钮时,调用LoginViewModel的方法onLoginByPhoneSmsCodeTriggered触发登录流程,该方法主要通知网域层进行处理,处理完成后根据结果更新登录状态,代码如下。

class LoginViewModel(
    private val loginUseCase: LoginUseCase = LoginUseCase(),
) : ViewModel() {
    private var _loginUiState = MutableStateFlow<LoginUiState>(LoginUiState.UnLogin)
    val loginUiState: StateFlow<LoginUiState> = _loginUiState

    fun onLoginByPhoneSmsCodeTriggered(phone:String, code :String){
        viewModelScope.launch {
            val result = loginUseCase(phone,code)
            _loginUiState.emit(LoginUiState.Login)
        }
    }
}

   从上面的代码可以看出,登录状态初始化为UnLogin,登录完成后把状态修改为Login。在登录界面会记录登录的状态,当状态发生变化后自动更新界面。

3)LoginUiState.kt

    登录状态是一个密封接口,类Login和UnLogin都继承该接口,分别表示登录和未登录状态。

sealed interface LoginUiState{
    data object Login : LoginUiState
    data object UnLogin : LoginUiState
}

以上是UI Layer的简单实现。

Domain Layer

    Domain Layer表示网域层,主要处理业务逻辑,当业务不是太复杂时,该层可以没有,相关的工作可以放到ViewModel处理。在本示例中,登录流程也不算太复杂,但是为了说明网域层的实现流程,还是在网域层实现一个简单的登录流程,代码如下。

class LoginUseCase(
    private val loginRepository: LoginRepository = DefaultLoginRepository(),
    ) {
    operator fun invoke(phone:String, code :String): String {
       return loginRepository.login(phone,code);
    }
}

    网域层一般有固定的实现方法,通常是实现一个类,类名为XXX+UseCase,表示一种用例,用于实现某个目的,如本例要实现登录的流程,使用LoginUseCase。

    在LoginUseCase的invoke方法中实现登录流程,这里调用数据层的接口进行处理并返回结果

Data Layer

    数据层主要实现提供数据的仓库,登录对应的仓库为LoginRepository,对应的接口如下。

interface LoginRepository {
     fun login(phone: String, code: String): String;
}

    根据不同的业务情况,仓库可以有不同的实现,下面的实现只是简单地返回登录的结果。

class DefaultLoginRepository(): LoginRepository {
    override  fun login(phone: String, code: String): String = "user-token:11111111"
}

    数据层除了定义仓库,还可以定义数据源DataSource,数据源可分本地数据源(LocalDataSource)和远程数据源(RemoteDataSource),前者从本地数据库获取数据,后者通过网络从服务器获取数据。如果有数据源,则调用数据源的接口进行处理,由于这些内容涉及到数据库组件和网络组件,这里只是简单返回结果表示登录成功。

    数据层返回数据后,UI层即可更新登录状态,如下图所示。

    本示例的工程已经上传到github,地址如下

 示例工程地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值