回忆MVC MVP MVVM

MVC

MVI 架构的理解

MVC架构首要分为以下几部分:

  • View层: 对应于xm布局文件和java代码动态view部分。

  • Controller层: 首要担任事务逻辑,在android中由Activity承当,但xml视图能力太弱,所以Activity既要担任视图的显现又要参加控制逻辑,承当功用过多。

  • Model层: 首要担任网络恳求,数据库处理,I/O操作,即页面的数据来源。

MVC数据流向为:

  • View接收用户的点击
  • View恳求Controller进行处理或直接去Model获取数据
  • Controller恳求model获取数据,进行其他的事务操作,将数据反馈给View层

MVC缺陷:

如上2所说,android中xml布局功用性太弱,activity实际上担任了View层与Controller层两者的功用,耦合性太高。

MVP:

MVI 架构的理解

MVP首要分为以下几部分:

1.View层:对应于Activity与xml,只担任显现UI,只与Presenter层交互,与Model层没有耦合。

2.Presenter层:首要担任处理事务逻辑,经过接口回调View层。

3.Model层:首要担任网络恳求,数据库处理的操作。

MVP处理了MVC的两个问题,即Activity承当了两层责任与View层和Model层耦合的问题。

MVP缺陷:

1.Presenter层经过接口与View通讯,实际上持有了View的引证。

2.事务逻辑的添加,一个页面变得杂乱,造成接口很庞大。

MVVM

MVI 架构的理解
MVVM改动在于将Presenter改为ViewModel,首要分为以下几部分:

1.View: Activity和Xml,与其他的相同

2.Model: 担任办理事务数据逻辑,如网络恳求,数据库处理,与MVP中Model相同

3.ViewModel:存储视图状况,担任处理体现逻辑,并将数据设置给可调查容器。

View和Presenter从双向依靠变成View能够向ViewModel发送指令,但ViewModel不会直接向View回调,而是让View经过调查者的形式去监听数据的改动,有用躲避MVP双向依靠的缺陷。

MVVM缺陷:

多数据流:View与ViewModel的交互涣散,短少仅有修改源,不易于追踪。

LiveData胀大:杂乱的页面需求界说多个MutableLiveData,而且都需求暴露为不可变的LivewData。

MVI是什么?

先上图

MVI 架构的理解
其首要分为以下几部分

  1. Model层: 与MVVM中的Model不同的是,MVIModel能够理解是View Model,存储视图状况,担任处理体现逻辑,并将ViewState设置给可调查数据容器

  2. View层: 与其他MVVM中的View一致,可能是一个Activity或许恣意UI承载单元。MVI中的View经过订阅Model的变化完结界面改写

  3. Intent层: 此Intent不是ActivityIntent,而是指用户的目的,比如点击加载,点击改写等操作,用户的任何操作都被包装成Intent,在model层调查用户目的然后去做加载数据等操作。

目前android干流的MVI是基于协程+flow+viewModel去完结的,协程应该大家都知道,所以先来了解一下MVI中的flow

flow是什么?

在flow 中,数据如水流相同经过上游发送,中间站处理,下流接收,类似于Rxjava,运用各种操作符完结异步数据流结构 代码示例:

   runBlocking {
            flow {
                emit(1)
                emit(2)
                emit(3)
                emit(4)
                emit(5)
            }.filter { it > 2 }
                .map { it * 2 }
                .take(2)
                .collect {
                    Log.d("FLOW", it.toString())
                }
        }

flow是冷流,只要订阅者订阅时,才开端履行发射数据流的代码 即下流无消费行为时,上游不会产生数据,只要下流开端消费,上游才从开端产生数据,从上述例子看,当调用了collect后,才会履行flow语句块里边的代码,而且flow每次从头订阅收集都会将一切事情从头发送一次

可是在咱们的开发场景中,一般是先触发某个事情(比如恳求数据之后)才会去改写UI,显然flow不适用于这种场景,因为flow只要在下流开端消费时才会触发出产数据

因而引进一个新的概念,StateFlow:

StateFlow与Flow的差异是StateFlow是暖流,即不论下流是否有消费行为,上游都会自己产生数据。 代码示例:

在ViewModel创立StateFlow,发送UI状况,关于UI状况下面会讲,这儿首要了解StateFlow的用法

//创立flow
private val _state = MutableStateFlow<ViewState>(ViewState.Default)
val state: StateFlow<EnglishState>
    get() = _state
//发送UI状况
state.value = ViewState.Loading

在Activity中接收:

    mViewModel.state.collect{
        when(it) {
            is ViewState.Default -> {
            }
            is ViewState.Loading -> {
                //展现加载中页面
                tvLoading.visibility = View.VISIBLE
            }
            is ViewState.BannerMsg -> {
                //加载完结,绑定数据
                tvLoading.visibility = View.GONE
                tvError.visibility = View.GONE
                mAdapter.setData(it.data)
            }
            is ViewState.Error -> {
                //加载失利,展现过错页面
                tvError.visibility = View.VISIBLE
            }
        }
}

看起来这个StateFlow用法和MVVM中的LiveData类似,那它们有什么差异呢?

差异1:StateFlow 需求将初始状况传递给构造函数,而 LiveData 不需求。

差异2:当 View 进入 STOPPED 状况时,LiveData.observe() 会自动撤销注册运用方,中止发送数据, 而从StateFlow收集数据的操作并不会自动中止。如需求完结LiveData相同的行为,能够在Lifecycle.repeatOnLifecycle块中去调查数据流。

MVI结构构建:

Intent介绍:

上面提到,intent指的是用户目的,在代码层面上来说他其实便是个枚举类,在kotlin中能够经过sealed关键字来生成关闭类,这个关键字生成的关闭类在when语句中能够不用写else

sealed class UserIntent {
    object GetBanners: UserIntent() //界说了一个用户获取Banner的目的
}

处理Intent

这儿需求了解一下:Chnnel

channel首要用于协程之间的通讯,运用send和receive往通道里写入或许读取数据,2个方法为非阻塞挂起函数,channel是暖流,不论有没有订阅者都会发送。 咱们的view层的触发操作和viewModel层获取数据这个流程恰巧应该是需求完全别离的,而且channel具有flow的特性,所以用channel来做view和viewModel的通讯十分合适 依据上面的例子,用Channel把UserIntent处理一下: 在View Model界说并调查用户目的:

class UserViewModel : ViewModel() {
    val userIntent = Channel<UserIntent>()  //界说用户目的
    init {
        observeUserIntent()
    }
    private fun observeUserIntent() {  //调查用户目的
        viewModelScope.launch {
            userIntent.consumeAsFlow().collect{
                when(it) {
                    is UserIntent.GetBanners -> {
                        loadBanner()
                    }
                }
            }
        }
    }

Activity中发送用户目的:

class MainActivity : AppCompatActivity() {
    private val mViewModel by lazy {
        ViewModelProvider(this)[UserViewModel::class.java]
    }
    private val mAdapter by lazy { BannerAdapter() }
    private lateinit var rvData: RecyclerView
    private lateinit var tvLoading: TextView
    private lateinit var tvError: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initView()
        loadData()
    }
    private fun initView() {
        rvData = findViewById<RecyclerView?>(R.id.rv_data).apply {
            layoutManager = LinearLayoutManager(this@MainActivity)
            adapter = mAdapter
        }
        tvLoading = findViewById(R.id.loading)
        tvError = findViewById(R.id.load_error)
    }
    private fun loadData() {  //将用户目的传给view Model
        lifecycleScope.launch {
            mViewModel.userIntent.send(UserIntent.GetBanners)
        }
    }

看完上面的代码,MVI中的View到Model之间的数据流向就现已明晰了, 接下来便是Model向View层传递数据的过程

State介绍:

State是UI状况,MVI的一个特点便是数据状况统一办理,state是个和Intent相同的枚举,可是不同的是intent是个事情流,state是个状况流 界说一个State类:

sealed class ViewState {
    object Default: ViewState() //页面默认状况
    object Loading : ViewState() //页面加载
    data class BannerMsg(val data: List<Banner>?): ViewState() //页面加载完结
    data class Error(val error: String?): ViewState() //页面加载过错
}

处理State

在ViewModel中观测到用户目的,依据用户目的去做相关操作,然后将UI State反馈给用户


class UserViewModel : ViewModel() {
    val userIntent = Channel<UserIntent>()
    private val _state = MutableStateFlow<ViewState>(ViewState.Default)
    val state: StateFlow<EnglishState>
        get() = _state
    init {
        observeUserIntent() 
    }
    private fun observeUserIntent() {
        viewModelScope.launch { //观测用户目的
            userIntent.consumeAsFlow().collect{
                when(it) {
                    is UserIntent.GetBanners -> { 
                        loadBanner()  
                    }
                }
            }
        }
    }
    private fun loadBanner() {
        viewModelScope.launch {
            state.value = ViewState.Loading //加载中状况 反馈给View层
            val banners = CloudService.cloudApi.getBanner() //获取数据
            banners.data?.let {
                state.value = ViewState.BannerMsg(it)  //加载成功状况,数据反馈给View
                return@launch
            }
            state.value = ViewState.Error(banners.errorMsg) //加载过错状况反馈给View
        }
    }
}

Activity中调查页面状况:

class MainActivity : AppCompatActivity() {
   private val mViewModel by lazy {
       ViewModelProvider(this)[UserViewModel::class.java]
   }
   private val mAdapter by lazy { BannerAdapter() }
   private lateinit var rvData: RecyclerView
   private lateinit var tvLoading: TextView
   private lateinit var tvError: TextView
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       initView()
       observeViewModel()
       loadData()
   }
   private fun initView() {
       rvData = findViewById<RecyclerView?>(R.id.rv_data).apply {
           layoutManager = LinearLayoutManager(this@MainActivity)
           adapter = mAdapter
       }
       tvLoading = findViewById(R.id.loading)
       tvError = findViewById(R.id.load_error)
   }
   private fun loadData() {
       lifecycleScope.launch { 
       //发送用户目的
           mViewModel.userIntent.send(UserIntent.GetBanners)
       }
   }
   private fun observeViewModel() {
       lifecycleScope.launch {
           mViewModel.state.collect{  //观测UI状况,依据不同的状况改写Ui
               when(it) {
                   is ViewState.Default -> {
                   //初始值不做任何操作
                   }
                   is ViewState.Loading -> {
                       //展现加载中页面
                       tvLoading.visibility = View.VISIBLE
                   }
                   is ViewState.BannerMsg -> {
                       //加载完结,绑定数据
                       tvLoading.visibility = View.GONE
                       tvError.visibility = View.GONE
                       mAdapter.setData(it.data)
                   }
                   is ViewState.Error -> {
                       //加载失利,展现过错页面
                       tvError.visibility = View.VISIBLE
                   }
               }
           }
       }
   }
}

MVI架构首要代码介绍完毕。

MVI总结:

MVI着重数据的单向活动,首要分为几步:

  • 用户操作以Intent的形式通知Model.

  • Model基于Intent更新State

  • View接收到State变化改写UI

数据永远在一个环形结构中单向活动,不能反向活动。

MVI优缺陷

长处:

  • MVI的中心思维是view-intent-viewmodel-state-view单向数据流,MVVM中心思维是view-viewmodel-view双向数据流
    • 代码分层更明晰,viewmodel无需关怀view怎么触发和更新,只需求维护intentstate即可
    • IntentState的引进处理了ViewModelModel的界限模糊问题

缺陷:

  • 单向流和双向流,并非 好和欠好 的挑选,而是 合适和不合适 的挑选,事务逻辑较为简单的界面,它不需求mvi、mvvm、mvp、mvc,只一个activity或许fragment + layout 即可,一味的套用架构,反而拔苗助长!

  • 逻辑、数据、UI 较为杂乱时,intentstate将会变得臃肿