总结和记录Android架构模式
前言
long long ago,写Android代码就是都堆到Activity中,充其量会封装一些方法放到其他的类里,来使得Activity看起来没那么臃肿;这样写对于很简单的页面当然没有问题,但复杂的页面即便封装了一些,Activity类依然十分臃肿(相信大部分人都见过老项目上千行的Activity类吧)。
为了解决这种问题,让代码更合理、职责更明确、扩展更容易,因此使用架构模式,来帮助我们更好的实现项目。
MVC
| 层 | Android中的角色划分 |
|---|---|
| model | 数据相关(数据库、接口数据) |
| view | xml\activity |
| controller | xxController\activity |
上图,是较为主流的MVC实现。虽然较非架构编码
职责更清晰,降低了耦合,但重点是Model层与View层是有关联的,Model会持有View的引用,并且Activity在控制层面和view层面仍有一些耦合。
为了解决activity内部耦合问题,实现模视分离,便有了MVP。
MVP
| 层 | Android中的角色划分 |
|---|---|
| model | 数据相关(数据库、接口数据) |
| view | xml\activity |
| presenter | xxPresenter |
上图,是MVP实现。由此可见,跟MVC是大致上相同的,区别在于实现了模视分离,职责更加清晰,仅通过Presenter这个中间商,串联起View和Model。在代码层面上,其实就是面向接口编程,通过IPresenter定义好操作函数;因此也会暴露出,如果逻辑过多时,接口会变得较为复杂,Presenter类也会变得复杂。
MVVM
| 层 | Android中的角色划分 |
|---|---|
| model | 数据相关(数据库、接口数据) |
| view | xml\activity |
| viewmodel | xxViewModel |
上图,是MVVM实现。这里的ViewModel并非指jetpack组件。MVVM架构的重点就是
双向绑定,通过DataBinding实现。这样进一步实现解耦,不需要我们自己再实现接口通知视图改变,视图改变后也会自动改变缓存数据。其缺点就是使用了DataBinding后,较难维护,而且出现问题较难定位。也因此大多数项目中还是采用了ViewBinding。
MVI
| 层 | Android中的角色划分 |
|---|---|
| model | UI状态、数据相关(数据库、接口数据) |
| view | xml\activity |
| intent | 用户意图 |
上图,是我认为的MVI实现。
图中内圈(红框黑字)部分,是cycle.js中的MVI架构,这也是MVI的来源。通过对其文档的了解,再类比到android MVI实现,能够辅助理解这种架构思想。
关键词:
响应式编程、界面状态、用户行为事件、数据单向流动、数据模型驱动界面、Google推荐的应用架构。
代码相关实现:
data class MyViewState(
val content: String = "等待",
val pageStatus: PageStatus = PageStatus.Success
)
/**
* 界面的事件 如弹出toast,弹出dialog
*/
sealed class MyViewEvent {
data class ShowToast(val message: String) : MyViewEvent()
}
/**
* 界面的状态,加载中,成功,失败
*/
sealed class PageStatus {
object Loading : PageStatus()
object Success : PageStatus()
data class Error(val throwable: Throwable) : PageStatus()
}
/**
* action 行为
*/
sealed class MyViewAction {
object FirstRequest : MyViewAction()
}
/**
* DEMO
* @author :curry
* @date :2022/1/13
*/
class MyViewModel : ViewModel() {
private val _viewStates = MutableLiveData(MyViewState())
val viewStates = _viewStates.asLiveData()
private val _viewEvents: SingleLiveEvents<MyViewEvent> = SingleLiveEvents()
val viewEvents = _viewEvents.asLiveData()
/**
* 匹配对应的行为,做对应的事儿
*/
fun dispatch(viewAction: MyViewAction) {
when (viewAction) {
is MyViewAction.FirstRequest -> firstRequest()
}
}
private fun firstRequest() {
/**
* 将操作的结果以界面应该呈现的状态返回
* 界面应该处理的操作如弹出框、toast定义为event 按event返回
*/
viewModelScope.launch {
_viewStates.setState {copy(pageStatus = PageStatus.Loading) }
delay(2000)
_viewStates.setState {copy(content = it, pageStatus = PageStatus.Success) }
_viewEvents.setEvent(NetworkViewEvent.ShowToast(it))
delay(2000)
_viewStates.setState {copy(pageStatus = PageStatus.Error(it))}
}
}
}
/**
* DEMO
* @author :curry
* @date :2022/1/13
*/
class MyActivity : AppCompatActivity() {
private val viewModel by viewModels<MyViewModel>()
...
private fun initViewModel() {
/**
* 监听状态,做对应的UI展现
*/
viewModel.viewStates.let { state ->
state.observeState(this, MyViewState::pageStatus) {
when (it) {
is PageStatus.Success -> //showContent()
is PageStatus.Loading -> //showLoading()
is PageStatus.Error -> //showError()
}
}
}
viewModel.viewEvents.observeEvent(this) {
when (it) {
is MyViewEvent.ShowToast -> toast(it.message)
}
}
}
/**
* 用户的点击事件,转成定义的Action,发给viewmodel
*/
fun simpleRequest(view: View) {
viewModel.dispatch(MyViewAction.FirstRequest)
}
}
参考: