MVC

模型

  • Model

指数据逻辑和实体模型

  • View

指布局文件

  • Controllor

指Activity,既要负责页面的展示和交互,还得负责数据的请求和业务逻辑之类的工作。

看起来MVC架构很清晰,但是实际的开发中,请求的业务代码往往被丢到了Activity里面,大家都知道layout.xml的布局文件只能提供默认的UI设置,所以开发中视图层的变化也被丢到了Activity里面,再加上Activity本身承担着控制层的责任。所以Activity达成了MVC集合的成就,最终我们的Activity就变得越来越难看,从几百行变成了几千行,维护的成本也越来越高。

MVP

MVP和MVC 相比而言,唯一的差别是Model和View之间不进行通讯,都是通过Presenter完成。

模型

  • Model

实体模型

  • View

Activity 或者Fragment,负责View的绘制以及与用户交互

  • Presenter

负责View与Model间的交互与逻辑处理

Presenter可以使View(Activity)不用直接和Model打交道,View(Activity)只用负责页面的显示和交互,剩下的和Model交互的事情都交给Presenter做,比如一些网络请求、数据的获取等,当Presenter获取到数据后再交给View(Activity)进行展示,这样,Activity的任务就大大减小了。

优缺点

优点

  • 分离了视图逻辑和业务逻辑,降低了耦合
  • 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性

缺点

  • 接口暴增,类膨胀问题。
  • 内存泄漏问题。由于P和V是互相引用,如果页面销毁时P还有正在进行的任务,那Activity无法回收,就发生了内存泄漏。

MVP 例子

具体操作思路:MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。在MVP模式中Activity的功能就是响应生命周期和显示界面,具体其他的工作都丢到了Presenter层中进行完成,Presenter其实是Model层和View层的桥梁。下面的例子解决了内存泄漏问题

BaseView

界面需要提供的UI方法中会有很多类似的UI方法,可以把它们提取到一个公共的父类接口中。比如提取显示loading界面和隐藏loading界面的方法,其他的view层接口就可以直接继承BaseView接口,不必重复的写显示和隐藏loading界面方法。

public interface BaseView {void  showLoading();void hideLoading();void showMessage(String message);
}

BasePresenter

共有的功能:添加view的绑定与销毁。解决内存泄漏问题。

public abstract class BasePresenter <V extends BaseView>{private V mView;/***  绑定 View* @param mView*/public void attachMView(V mView){this.mView=mView;}/*** 解绑View*/public void detachMView(){mView=null;}public V getMView() {return mView;}
}

BaseActivity

共有的功能:Presenter绑定到activity,View的绑定和解绑操作。

public abstract class BaseActivity<V extends BaseView,P extends BasePresenter<V>> extends AppCompatActivity {protected P mPresenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (mPresenter==null){mPresenter=initPresenter();}mPresenter.attachMView((V) this);}@Overrideprotected void onDestroy() {super.onDestroy();if (mPresenter!=null){mPresenter.detachMView();}}// 初始化presenterprotected abstract P initPresenter();
}

登录为例

LoginView
public interface LoginView extends BaseView {void onResultSuccess(User user);void onResultFail(String errorMessage);
}
LoginPresenter
public class LoginPresenter extends BasePresenter<LoginView> {public void request_login(String name, String pwd) {NetWorkManager.getInstance().getApiService().login(name, pwd).compose(SchedulerProvider.getInstance().applySchedulers()).subscribe(new Observer<ApiResponse<User>>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {if (getMView()!=null){getMView().showLoading();}}@Overridepublic void onNext(@NonNull ApiResponse<User> userApiResponse) {if (getMView()!=null){getMView().hideLoading();if (userApiResponse.getErrorCode()==0){getMView().onResultSuccess(userApiResponse.getData());}else {getMView().showMessage(userApiResponse.getErrorMsg());}}}@Overridepublic void onError(@NonNull Throwable e) {if (getMView()!=null){getMView().hideLoading();getMView().onResultFail(e.getMessage());}}@Overridepublic void onComplete() {}});}
}
LoginActivity
public class LoginActivity extends BaseActivity<LoginView,LoginPresenter> implements LoginView{private EditText etName;private EditText etPassword;private Button btn_login;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);initView();}@Overrideprotected LoginPresenter initPresenter() {return new LoginPresenter();}private void initView() {etName = findViewById(R.id.etName);etPassword = findViewById(R.id.etPassword);btn_login = findViewById(R.id.btn_login);btn_login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {login();}});}private void  login(){String username=etName.getText().toString();String passWord=etPassword.getText().toString();mPresenter.request_login(username,passWord);}@Overridepublic void onResultSuccess(User user) {showMessage(user.toString());}@Overridepublic void onResultFail(String errorMessage) {}@Overridepublic void showLoading() {}@Overridepublic void hideLoading() {}@Overridepublic void showMessage(String message) {Toast.makeText(this,message,Toast.LENGTH_SHORT).show();}}

以上就是以登录为例MVP例子。完整代码

效果图:

MVVM

MVVM 通过双向绑定的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念。

模型

  • View

主要是指Activity或者Fragment,负责页面的展示以及View的变化,不参与任何逻辑和数据的处理。

  • ViewModel

主要负责业务逻辑和数据处理,本身不持有View层引用。

  • Model

实体类JavaBean。主要负责从本地数据库或者远程服务器来获取数据。

优缺点

优点

  • 双向绑定技术,当Model变化时,View-Model会自动更新,View也会自动变化。
  • View的功能进一步的强化,具有控制的部分功能。

缺点

  • 数据绑定增加Bug调试难度
  • 数据双向绑定不利于View重用

小结

MVVM的本质是数据驱动,把解耦做的更彻底,viewModel不持有view。View产生事件,使用 ViewModel进行逻辑处理后,通知Model更新数据,Model把更新的数据给ViewModel,ViewModel自动通知View更新界面,而不是主动调用View的方法。

MVVM例子

采用google官方推荐的MVVM 框架:

View 层:BaseActivity

在BaseActivity 主要处理了初始化viewModel, viewModel 和lifecycle 生命周期绑定 等工作。

open class BaseActivity <VM : BaseViewModel>: AppCompatActivity(){lateinit var mViewModel: VMoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)initViewModel()createObserve()}/** 提供编写LiveData监听逻辑的方法 */open fun createObserve() {mViewModel.apply {exception.observe(this@BaseActivity) {LogUtil.e("网络请求错误:${it.message}")when (it) {is SocketTimeoutException -> ToastUtil.showShort(this@BaseActivity,"网络超时")is ConnectException, is UnknownHostException -> ToastUtil.showShort(this@BaseActivity,"网络连接异常")else -> ToastUtil.showShort(this@BaseActivity, it.message ?: "网络错误")}}complete.observe(this@BaseActivity){// todo 请求完成工作}}}open fun providerVMClass(): Class<VM>? = nullprivate fun initViewModel() {providerVMClass()?.let {mViewModel = ViewModelProvider(this).get(it)lifecycle.addObserver(mViewModel)}}override fun onDestroy() {super.onDestroy()lifecycle.removeObserver(mViewModel)}
}

providerVMClass()方法中通过BaseViewModel子类泛型类型参数获取Class,在通过 ViewModelProviders.of(this).get(it)实例化ViewModel。

ViewModel 层:BaseViewModel

open class BaseViewModel :ViewModel(),LifecycleObserver{/** 请求异常(服务器请求失败,譬如:服务器连接超时等) */val exception = MutableLiveData<Exception>()/** 请求完成 */val complete=MutableLiveData<Int>()/*** 启动协程,封装了viewModelScope.launch** @param tryBlock try语句运行的函数**/fun launch(tryBlock: suspend CoroutineScope.() -> Unit) {// 默认是执行在主线程,相当于launch(Dispatchers.Main)viewModelScope.launch {try {tryBlock()} catch (e: Exception) {exception.value = e} finally {complete.value=0}}}
}

BaseViewModel 里主要做了协程请求数据状态封装。使用LiveData及时通知数据更新。

Model(Repository) 层:BaseRepository

主要是获取ApiService和网络请求订阅容器,方便管理网络请求。

open class BaseRepository {suspend fun <T> apiCall(api: suspend () -> ApiResponse<T>): ApiResponse<T> {return withContext(Dispatchers.IO) { api.invoke() }}
}

以登录为例

LoginViewModel
class LoginViewModel :BaseViewModel() {// 获取 用户名val userName =ObservableField<String>()// 获取 密码val passWord = ObservableField<String>()// 登录结果liveDataprivate var  loginResultData=MutableLiveData<ApiResponse<User>>()// 对外提供获取登录结果方法fun getLoginResult()=loginResultDatafun login(userName:String,pwd:String)=launch{// 网络请求val loginResult=ApiManager.login(userName,pwd)// 传递登录结果值loginResultData.postValue(loginResult)}
}

LoginViewModel中持有数据观察容器LiveData和真正发起网络请求动作,在接收到服务端返回的数据通过loginResultData.postValue(loginResult)通知Observer数据的更改,此处需注意的是,setValue方法只能在主线程中调用,postValue可以在任何线程中调用,如果是在后台子线程中更新LiveData的值,必须调用postValue。

LoginActivity
class LoginActivity : BaseActivity<LoginViewModel>() {private lateinit var mBinding: ActivityLoginBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 使用 DataBinding 双向绑定mBinding=DataBindingUtil.setContentView<ActivityLoginBinding>(this,R.layout.activity_login)initView()// DataBinding 关联ViewModelmBinding.viewModel=mViewModel}override fun providerVMClass(): Class<LoginViewModel>? {return LoginViewModel::class.java}private fun initView() {mBinding.btnLogin.setOnClickListener {// 发起登录请求mViewModel.login(mViewModel.userName.get()!!,mViewModel.passWord.get()!!)}// 登录结果mViewModel.getLoginResult().observe(this){ToastUtil.showShortInCenter(this@LoginActivity, it.errorMsg)}}}

LoginActivity的工作是UI初始化,发请网络请求以及数据观察更新UI。

activity_login
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><variablename="viewModel"type="com.xf.mvvmexample.ui.LoginViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="15dp"android:layout_marginTop="30dp"android:layout_marginEnd="15dp"android:hint="请输入名字"android:text="@={viewModel.userName}"android:textSize="15sp" /><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="15dp"android:layout_marginTop="30dp"android:layout_marginEnd="15dp"android:inputType="textPassword"android:hint="请输入密码"android:text="@={viewModel.passWord}"android:textSize="15sp" /><Buttonandroid:id="@+id/btn_login"android:layout_width="match_parent"android:layout_height="50dp"android:layout_marginTop="50dp"android:textColor="@color/white"android:text="登录"android:layout_marginEnd="15dp"android:layout_marginStart="15dp" /></LinearLayout>
</layout>

使用DataBinding进行数据和UI双向绑定,及时获取数据。以上就是MVVM架构,实现登录功能。完整代码

效果:

浅析MVC、MVP、MVVM 架构相关推荐

  1. Android架构设计之MVC/MVP/MVVM浅析

    目录 写在前面 一.案例演示 二.MVC模式 2.1.MVC简介 2.2.MVC模式的使用 2.3.MVC模式的缺点 三.MVP模式 3.1.MVP简介 3.2.MVP模式的作用 3.3.MVP模式的 ...

  2. java mvc mvp mvvm_一篇文章了解架构模式:MVC/MVP/MVVM

    架构模式的文章很多,好理解的没有几个.大部分文章出现的主要问题有: 没有设定好作用域:前端MVC是改造过的MVC,和后台MVC有明显的区别,不能一概而论 没有实际的例子:实际的例子对应日常的工作,没有 ...

  3. Android App的设计架构:MVC,MVP,MVVM与架构经验谈

    本文转载自https://www.tianmaying.com/tutorial/AndroidMVC,原文作者周鸿博. 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开 ...

  4. iOS-【转载】架构模式 - 简述 MVC, MVP, MVVM 和 VIPER

    看了很多篇关于 iOS 架构模式的,尤其是关于 MVVM 的,都是似懂非懂,无意看见了这篇,发现总结的很到位,很用心,特转载至此,如果英语好,请看原文 iOS Architecture Pattern ...

  5. iOS-【转载】架构模式 - 简述 MVC, MVP, MVVM 和 VIPER (译)

    看了很多篇关于 iOS 架构模式的,尤其是关于 MVVM 的,都是似懂非懂,无意看见了这篇,发现总结的很到位,很用心,特转载至此,如果英语好,请看原文 iOS Architecture Pattern ...

  6. iOS 架构模式 - 简述 MVC, MVP, MVVM

    Make everything as simple as possible, but not simpler - Albert Einstein,把每件事,做简单到极致,但又不过于简单 - 阿尔伯特· ...

  7. iOS 架构模式 - 简述 MVC, MVP, MVVM 和 VIPER (译)

    在使用 iOS 的 MVC 时候感觉怪怪的?想要尝试下 MVVM?之前听说过 VIPER,但是又纠结是不是值得去学? 继续阅读,你就会知道上面问题的答案 - 如果读完了还是不知道的话,欢迎留言评论. ...

  8. [iOS][转]iOS 架构模式 - 简述 MVC, MVP, MVVM 和 VIPER (译)

    转自: https://segmentfault.com/a/1190000004680605?ref=myread Make everything as simple as possible, bu ...

  9. MVC---Android App的设计架构:MVC,MVP,MVVM与架构经验谈

    转载自: http://www.tianmaying.com/tutorial/AndroidMVC 1.架构设计的目的 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使 ...

  10. 表现层持续解耦带来的模式转变 MVC MVP MVVM

    ---微软WPF带来的团队变化和软件技术变化  Model-View-ViewModel是一种架构模式,主要在WPF.Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏( ...

最新文章

  1. matlab模型参数不匹配怎么办,修改Simulink模型后出现初始状态大小不匹配的错误...
  2. setTimeOut()和setInterval()的用法
  3. 【深度学习】计算机视觉相关技术探索(一)
  4. linux安装包文件格式,linux多种安装包格式的安装方法
  5. Ajax异步请求(重渲染DOM元素时,如何自动调用并执行JS自定义函数【含代码】)- 案例篇
  6. 【IoT平台技术对接分享】如何上传正确的消息推送证书
  7. php使用自定义alert,IOS_iOS自定义alertView提示框实例分享,本文实例为大家分享iOS自定义a - phpStudy...
  8. C语言 gcc API
  9. Spark 编程工具类与工具方法(一)—— 欧式距离
  10. 将执行文件转化为bat批处理文件的工具(批处理文件方式提供)
  11. 用AjaxPro实现二级联动
  12. 2018.07.09 顺序对齐(线性dp)
  13. Angular和Vue.js 深度对比
  14. 动作捕捉系统用于模仿学习
  15. ASEMI快恢复二极管RL257参数,RL257图片,RL257应用
  16. PHP语言之字符串与函数 educoder答案
  17. jquery validate插件onfocosout/onkeyup出错的解决方法
  18. Fedora修复grub2启动项grub rescue
  19. 四、Vue.js 模板语法
  20. 如何使用ffmpeg分离视频和音频t

热门文章

  1. matlab的GARCH模型模拟,GARCH模型在Matlab中的完成.doc
  2. repo - contains uncommitted changes
  3. 华为鸿蒙手机系统什么时候开始更新_华为鸿蒙OS适配计划曝光,一起看看你的手机什么时候可以升级...
  4. linux busybox启动脚本顺序,详解Busybox及如何安装的过程
  5. 基于滑模控制的永磁同步电机直接转矩控制学习
  6. iOS:触摸事件、手势识别、摇晃事件、耳机线控
  7. 方正品牌机系统安装指南
  8. 智能电销外呼系统,帮助电销人员告别传统模式的低效
  9. 识破人情惊破胆,看穿世间心胆寒
  10. 一文搞懂Kerberos