浅谈MVC,MVP,MVVM,MVI三种架构模式

一.MVC架构模式

View:对应的xml布局文件
Model:实体模型(JavaBean)
Controllor:对应于Activity业务逻辑,数据处理和UI处理

该模式的特点,就是当某个界面非常复杂的时候,activity代码量会上千行的增加,非常冗余。

二.MVP架构模式

View:对应的xml布局文件
Model:实体模型(JavaBean)
Presenter:责完成View于Model间的交互和业务逻辑

搭建MVP的一个栗子

(1).BaseView接口

interface BaseView {
     void showLoading();
     void hideLoading();
}

(2).创建一个持有BaseView接口的泛型基类BasePersenter

  • 定义泛型对象引用 T mView ,方便HomePresenter()构造方法赋值
  • compositeDisposable 管理订阅请求
  • detachView 解除订阅,释放mView
public  class BasePrenster<T extends BaseView> {
    T mView; //方便构造方法赋值
   CompositeDisposable compositeDisposable=new CompositeDisposable(); //管理订阅请求
   public void detachView(){
       compositeDisposable.dispose();  //compositeDisposable.clear();//取消订阅
       mView=null;
   }
}

(3).新建一个HomePresenter继承BasePrenster<HomeView>,定义getHomeData方法,请求数据

class HomePresenter extends BasePrenster<HomeView> {
   public HomePresenter(HomeView homeView){
       this.mView=homeView;
   }
    public void getHomeData() {
        mView.showLoading();
        compositeDisposable.add(HttpRetrofit.INSTANCE.getApiService().getTopArticles()
                .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new OConsumer<HttpResult<ArrayList<HomeData.DatasBean>>>(new 
                SuccessCallBack<HttpResult<ArrayList<HomeData.DatasBean>>>() {
                    @Override
                    public void onSuccess(HttpResult<ArrayList<HomeData.DatasBean>> httpResult) {
                        mView.onHomeDataSuccess(httpResult);
                        mView.hideLoading();
                    }
                }), new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        mView.hideLoading();
                        mView.onHomeDataFailed(throwable.getMessage());
                    }
                }));
    }

}

(4).新建一个持有presenter的泛型抽象基类BaseMvpActivity

 public abstract class  BaseMvpActivity<P extends BasePrenster> extends AppCompatActivity {
    protected  P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter=createPresenter();
        initData();
    }

    protected abstract P createPresenter();
    protected abstract void initData();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter!=null){
            mPresenter.detachView();
        }
    }
}

(5).新建一个HomeActivity继承自BaseMvpActivity的,实现抽象方法createPersenter(),initData

  • createPresenter() 直接 new HomePresenter(this),将HomeView作为参数传递.
  • initData() 通过mPresenter.getHomeData请求Home页面数据.
  • showLoading 显示Loading
  • hideLoading 隐藏Loading
  • onHomeDataSuccess 请求成功回调
  • onHomeDataFailed 请求失败回调
class HomeActivity extends BaseMvpActivity<HomePresenter> implements HomeView{
    @Override
    protected HomePresenter createPresenter() {
        return new HomePresenter(this);
    }
    @Override
    protected void initData() {
        mPresenter.getHomeData();
    }

    @Override
    public void showLoading() {
    }

    @Override
    public void hideLoading() {
    }

    @Override
    public void onHomeDataSuccess(HttpResult<ArrayList<HomeData.DatasBean>> httpResult) {
    }

    @Override
    public void onHomeDataFailed(String errorMsg) {
    }
}
核心就是Activity实现View的接口,并持有Presenter对象,Presenter中持有Model和View对象,负责完成View于Model间的交互和业务逻辑,本文中的Prsenter并没有Model,而是直接新建了一些类似model中的请求方法,这样可以省掉Model接口和ModelIml接口实现类,简化代码。

三.MVVM架构

-View:对应的xml布局文件
-Model:实体模型(JavaBean)
-ViewModel:责完成View于Model间的交互和业务逻辑(依赖于LiveData)

1.新建一个BaseViewModel 继承 ViewModel.
open class BaseViewModel: ViewModel() {
    // private val _state = MutableStateFlow("")
    val showLoadingLiveData by lazy { UnPeekLiveData<String>() }
    val dismissDialogLiveData by lazy { UnPeekLiveData<String>() }

    fun <T:Any> request(
        block: suspend () -> HttpResult<T>,
        success: (HttpResult<T>) -> Unit,
        error: (Throwable) -> Unit={},
        isShowDialog: Boolean = false,
        loadingMessage: String = "loading..."
    ): Job {
        //如果需要弹窗 通知Activity/fragment弹窗
        return viewModelScope.launch {
            runCatching  {
                if(isShowDialog){
                    showLoadingLiveData.value=loadingMessage
                }
                block()
            }.onSuccess {
                dismissDialogLiveData.value="close"
                success(it)
            }.onFailure {
                dismissDialogLiveData.value="close"
                error(it)
            }
        }
    }
}
2.新建一个泛型基类BaseVMActivity<VM :BaseViewModel>,内部持有ViewModel对象
abstract class BaseVMActivity<VM : BaseViewModel>:AppCompatActivity() {
    lateinit var mViewModel:VM

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mViewModel=createViewModel()
        initData();
    }

    abstract fun createViewModel(): VM
    abstract fun initData()
}
3.新建一个HomeViewModel继承 BaseViewModel
class HomeViewModel : BaseViewModel() {
    val homeLiveData= MutableLiveData<ListDataUiState<ArrayList<HomeData.DatasBean>>>()
    fun getHomeList() {
        request(
            block = {  HttpRetrofit.apiService.getHomeList() },
            success = {
                val listDataUiState=ListDataUiState<ArrayList<HomeData.DatasBean>>(
                    dataBean =it
                )
                homeLiveData.value=listDataUiState
            },
            error = {
                val listDataUiState=ListDataUiState<ArrayList<HomeData.DatasBean>>(
                    isException = true,
                    error = it
                )
                homeLiveData.value=listDataUiState
            },
            isShowDialog = true,
            loadingMessage = "加载中..."
        )
    }
}

4.新建一个HomeActivity继承BaseVMActivity
class HomeActivity:BaseVMActivity<HomeViewModel>() {
    lateinit var mDataBind:ActivityHomeBinding

    override fun createViewModel(): HomeViewModel {
         return ViewModelProvider(this).get(HomeViewModel::class.java);
    }

    override fun initData() {
        mDataBind = DataBindingUtil.setContentView(this, R.layout.activity_home) //实例化mDataBind
        mViewModel.getHomeList() //请求数据
        //添加观察者,处理数据
        mViewModel.homeLiveData.observe(this) { //处理数据
            mDataBind.homeData=it;//数据双向绑定
       }
    }
}

四. MVI模式**

在Android开发中,MVI(Model-View-Intent)架构是一种基于响应式编程思想的架构模式,它将应用分为Model(模型)View(视图)Intent(意图)三部分,并通过单向数据流来管理应用的状态和交互。以下是MVI架构的详细实现方式:

4.1. 核心组件设计

(1)Model(模型)

  • 职责:管理应用的状态和业务逻辑。
  • 实现方式
    • 使用不可变数据类(如Kotlin的data class)表示应用状态。
    • 状态通常是一个State类,包含所有可能的UI状态。
    • 示例:
      data class WeatherState(
          val isLoading: Boolean = false,
          val error: String? = null,
          val weatherData: WeatherData? = null
      )
      

(2)View(视图)

  • 职责:展示数据并响应用户交互。
  • 实现方式
    • View是被动观察者,订阅状态变化并更新UI。
    • 通常使用LiveDataStateFlowRxJavaObservable来观察状态。
    • 示例:
      class WeatherActivity : AppCompatActivity() {
          private val viewModel: WeatherViewModel by viewModels()
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_weather)
      
              viewModel.state.observe(this) { state ->
                  when {
                      state.isLoading -> showLoading()
                      state.error != null -> showError(state.error)
                      state.weatherData != null -> showWeather(state.weatherData)
                  }
              }
      
              findViewById<Button>(R.id.refreshButton).setOnClickListener {
                  viewModel.dispatchIntent(WeatherIntent.RefreshWeather)
              }
          }
      
          private fun showLoading() { /* 显示加载动画 */ }
          private fun showError(error: String) { /* 显示错误信息 */ }
          private fun showWeather(data: WeatherData) { /* 更新UI */ }
      }
      

(3)Intent(意图)

  • 职责:表示用户的操作或系统事件。
  • 实现方式
    • 使用密封类(sealed class)定义所有可能的用户操作。
    • Intent是View与Model交互的唯一方式。
    • 示例:
      sealed class WeatherIntent {
          //穷举所有可能的用户操作
          object RefreshWeather : WeatherIntent()
          object LoadMoreWeather : WeatherIntent()
          data class Search(val keyword: String) : WeatherIntent() // 搜索意图
      }
      

4.2. 数据流管理

  • 单向数据流

    1. View捕获用户操作并生成Intent
    2. ViewModel接收Intent并更新State
    3. View观察State变化并更新UI。
  • 状态管理

    • 使用StateFlowLiveData来存储和分发状态。
    • 示例(使用StateFlow):
      class WeatherViewModel : ViewModel() {
          private val stateFlow= MutableStateFlow(WeatherState())
      
          fun dispatchIntent(intent: WeatherIntent) {
              when (intent) {
                  is WeatherIntent.RefreshWeather -> fetchWeather()
              }
          }
      
          private fun fetchWeather() {
              viewModelScope.launch {
                  stateFlow.value = stateFlow.value.copy(isLoading = true)
                  try {
                      val weatherData = weatherRepository.getWeather()
                      stateFlow.value = stateFlow.value.copy(isLoading = false, weatherData = weatherData)
                  } catch (e: Exception) {
                      stateFlow.value = stateFlow.value.copy(isLoading = false, error = e.message)
                  }
              }
          }
      }
      

4.3. 关键技术点

(1)不可变数据

  • 使用data classcopy方法确保状态不可变。
  • 示例:
    val newState = oldState.copy(isLoading = false, weatherData = data)
    

(2)协程与异步操作

  • 使用Kotlin协程处理异步任务(如网络请求)。
  • ViewModel中使用viewModelScope确保协程在生命周期内自动取消。

(3)依赖注入

  • 使用Dagger Hilt或Koin注入依赖(如Repository)。
  • 示例(Hilt):
    @HiltViewModel
    class WeatherViewModel @Inject constructor(
        private val weatherRepository: WeatherRepository
    ) : ViewModel()
    

(4)测试

  • 单元测试
    • 测试Reducer(状态更新逻辑)。
    • 测试Intent处理逻辑。
  • 集成测试
    • 测试ViewModelRepository的交互。

4.4. 示例:天气应用完整流程

  1. 用户点击刷新按钮 → View生成WeatherIntent.RefreshWeather
  2. ViewModel接收Intent → 调用fetchWeather()
  3. Repository发起网络请求 → 返回WeatherData
  4. ViewModel更新State → 设置isLoading = false并填充weatherData
  5. View观察State变化 → 更新UI显示天气信息。

4.5. 优势

  • 可预测性:单向数据流使状态变化可追踪。
  • 可测试性:状态更新逻辑与UI解耦,易于单元测试。
  • 可维护性:清晰的组件职责划分,减少耦合。

6. 注意事项

  • 状态膨胀:复杂页面可能导致状态类过于庞大,需合理拆分。
  • 内存开销:频繁状态更新可能增加内存使用,需优化状态更新逻辑。

通过以上方式,MVI架构可以帮助开发者构建响应式、可维护的Android应用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容