Markdown版本笔记 | 个人GitHub首页 | 个人博客 | 个人微信 | 个人邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
其实Android中只有MVjava
Android下MVC中的控制层是由Activity来承担的,Activity原本主要是做为初始化页面,展现数据的操做,可是由于XML视图功能太弱,因此Activity既要负责视图的显示又要加入控制逻辑,承担的功能过多。android
好比对于登陆页面,MVC的基本流程为:git
用户与View交互,View接收并反馈用户的动做,View把用户的请求传给相应的控制器,由控制器决定调用哪一个模型,而后由模型调用相应的业务逻辑对用户请求进行加工处理,若是须要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图渲染返回的数据。github
在Android开发中,Activity并非一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操做请求,进而做出响应。随着界面及其逻辑的复杂度不断提高,Activity类的职责不断增长,以至变得庞大臃肿。算法
好比在Android中,对于登陆页面,典型的交互过程是这样的:数据库
用户点击登陆按钮 → Activity中注册的监听器检测到点击事件 → Activity经过转动View中的ProgressBar来响应用户点击事件 → Activity经过View中的EditText获取用户输入的帐户密码 → Activity将数据交由业务逻辑层(Model层)处理 → Model层处理完成后经过回调将数据返回给Activity → Activity更新UI反馈给用户后端
由上面的案例能够看出,其实这个View对应于布局文件能作的事情特别少,实际上关于该布局文件中的数据绑定、事件处理的代码都在Activity中,形成了Activity既像View又像Controller,这可能也就是为什么:设计模式
Most of the modern Android applications just use View-Model
architecture,everything is connected with Activity.缓存
BaseModel顾名思义就是全部业务逻辑model的父类,这里的onDestroy()
方法用于跟activity或者fragment生命周期同步,在destroy作一些销毁操做微信
public interface BaseModel { void onDestroy(); }
Callback是根据View或者Controller调用Model时回调的参数个数选择使用
public interface Callback1<T> { void onCallBack(T t); }
public interface Callback2<T, P> { void onCallBack(T t, P p); }
SampleModel是咱们业务逻辑的具体实现
public class SampleModel implements BaseModel { public void getUserInfo(String uid, Callback1<UserInfo> callback) { UserInfo userInfo = new UserInfo(); //...从网络或数据库等获取数据 callback.onCallBack(userInfo); } public void getUserInfo2(String uid, Callback2<UserInfo, String> callback) { UserInfo userInfo = new UserInfo(); //...从网络或数据库等获取数据 callback.onCallBack(userInfo, "其余数据"); } @Override public void onDestroy() { } public class UserInfo { private int age; private String name; //... } }
public class SampleActivity extends AppCompatActivity { private SampleModel sampleModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); sampleModel = new SampleModel(); String uid = "123456"; findViewById(R.id.button).setOnClickListener(view -> getUserInfo(uid)); } @Override protected void onDestroy() { super.onDestroy(); sampleModel.onDestroy(); } //获取用户信息 private void getUserInfo(String uid) { sampleModel.getUserInfo(uid, userInfo -> { //...设置用户信息到view }); } }
事件的流向
总结
MVP的核心就是:让M和V彻底解耦,经过Presenter统一调度管理
MVP的基本流程:Presenter从View中获取须要的参数,交给Model去处理,Model执行过程当中的反馈以及结果告诉Presenter,Presenter再让View作对应的显示。
MVP跟MVC很相像,根据MVC的发展来看,咱们把MVP当成MVC来看也不为过,由于MVP也是三层,惟一的差异是Model和View之间不进行通信
,都是经过Presenter完成。
前面介绍MVC的时候提到了算是致命缺点吧,在android中因为activity(god object)的存在,Controller和View很难作到彻底解耦
。但在MVP中就能够很好的解决这个问题
让Model和View彻底解耦,由Presenter负责完成View与Model的交互
将Actvity视为View层
,减小了Activity的职责,将复杂的逻辑代码提取到了Presenter中
Presenter与View之间的交互彻底是经过接口的
分析这个模块须要哪些业务逻辑,或者说有哪些复杂的功能,以此定义Presenter接口。
对于登陆模块,主要的就是登陆功能。为了增长接口的复杂度,这里我又添加了一个退出前清理功能。
public interface Login_Presenter_I { void login(String username, String password);//登陆过程可能涉及到很工做,因此把它抽出来。此过程须要与View交互 void onFinishActivity();//退出前可能要作不少清理工做,因此也把它抽出来。此过程不须要与View交互 }
分析上述Presenter层中的功能在被Model层处理过程当中,Model须要通知Presenter哪些内容,以此定义Model接口。
Model层在执行过程当中,是经过接口通知Presenter执行过程当中的状态以及执行完毕后的结果的,为了逻辑更清晰,建议此此接口定义为Model层接口的内部接口。
public interface Login_Model_I { //参数 listener:Model层经过此接口通知Presenter执行过程当中的状态以及执行完毕后的结果 void login(String username, String password, OnLoginListener listener);//执行过程当中须要通知Presenter void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//执行过程当中不须要通知Presenter //将Model层须要通知Presenter的内容,按照类别,定义在不一样的接口中 interface OnLoginListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } }
分析全部可能会操做UI的最基础逻辑,以此定义View接口。
Presenter层是经过接口来操做View层的。
public interface Login_Activity_I { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void showToast(String msg, int duration); }
public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener { private Login_Activity_I loginActivityI; //拿到的是接口,整个Presenter中没有导入任何View和Activity private Login_Model_I loginIModel; //拿到的是Model层的实现类 public Login_Presenter_Impl(Login_Activity_I loginActivityI) { this.loginActivityI = loginActivityI;//View层的实现类,由Activity传过来 this.loginIModel = new Login_Model_Impl();//Model层的实现类,由Activity传过来或本身建立都可 } @Override public void login(String username, String password) { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI loginIModel.login(username, password, this);//交给Model层处理 } @Override public void onFinishActivity() { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI boolean clearSp = new Random().nextBoolean(); boolean deleteCache = new Random().nextBoolean(); loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交给Model层处理 if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI } @Override public void onUsernameError() { if (loginActivityI != null) { loginActivityI.setUsernameError(); loginActivityI.hideProgress(); } } @Override public void onPasswordError() { if (loginActivityI != null) { loginActivityI.setPasswordError(); loginActivityI.hideProgress(); } } @Override public void onSuccess() { if (loginActivityI != null) loginActivityI.showToast("登陆成功", Toast.LENGTH_SHORT); } }
Model层的实现类,只处理逻辑,彻底不操做View,对逻辑处理的状态反馈给Presenter
public class Login_Model_Impl implements Login_Model_I { @Override public void login(String username, String password, OnLoginListener listener) { if (TextUtils.isEmpty(username)) listener.onUsernameError(); else if (TextUtils.isEmpty(password)) listener.onPasswordError(); else listener.onSuccess(); } @Override public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) { if (clearSp) Log.i("bqt", "【清理SP】"); if (deleteCache) Log.i("bqt", "【清理缓存】"); } }
public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener { private ProgressBar progressBar; private EditText username; private EditText password; private Login_Presenter_I presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); presenter = new Login_Presenter_Impl(this);//new一个Presenter的实现类,把本身传过去。实际上接收的只是LoginView接口的实例 } @Override public void finish() { presenter.onFinishActivity(); super.finish(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError(getString(R.string.username_error)); } @Override public void setPasswordError() { password.setError(getString(R.string.password_error)); } @Override public void showToast(String msg, int duration) { Toast.makeText(this, msg, duration).show(); } @Override public void onClick(View v) { //点击登陆时,View把数据传给presenter,presenter处理完数据后通知View处理事件 presenter.login(username.getText().toString(), password.getText().toString()); } }
public interface LoginView { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void navigateToHome(); }
public class LoginActivity extends AppCompatActivity implements LoginView { private ProgressBar progressBar; private EditText username; private EditText password; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = findViewById(R.id.progress); username = findViewById(R.id.username); password = findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(v -> login()); presenter = new LoginPresenter(this, new LoginModel()); } @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError("用户名错误"); } @Override public void setPasswordError() { password.setError("密码错误"); } @Override public void navigateToHome() { startActivity(new Intent(this, MainActivity.class)); finish(); } private void login() { presenter.login(username.getText().toString(), password.getText().toString()); } }
public class LoginPresenter implements LoginModel.OnLoginFinishedListener { private LoginView LoginView; private LoginModel loginModel; LoginPresenter(LoginView LoginView, LoginModel loginModel) { this.LoginView = LoginView; this.loginModel = loginModel; } public void login(String username, String password) { if (LoginView != null) { LoginView.showProgress(); } loginModel.login(username, password, this); } public void onDestroy() { LoginView = null; } @Override public void onUsernameError() { if (LoginView != null) { LoginView.setUsernameError(); LoginView.hideProgress(); } } @Override public void onPasswordError() { if (LoginView != null) { LoginView.setPasswordError(); LoginView.hideProgress(); } } @Override public void onSuccess() { if (LoginView != null) { LoginView.navigateToHome(); } } }
public class LoginModel { interface OnLoginFinishedListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } public void login(final String username, final String password, final OnLoginFinishedListener listener) { new Handler().postDelayed(() -> { if (TextUtils.isEmpty(username)) { listener.onUsernameError(); return; } if (TextUtils.isEmpty(password)) { listener.onPasswordError(); return; } listener.onSuccess(); }, 2000); } }
MVP中,随着业务逻辑的增长,UI的改变多的状况下,会有很是多的跟UI相关的case,这样就会形成View的接口会很庞大。而MVVM就解决了这个问题,经过双向绑定
的机制,实现数据和UI内容,只要想改其中一方,另外一方都可以及时更新的一种设计理念,这样就省去了不少在View层中写不少case的状况,只须要改变数据就行。
先看下MVVM设计图:
这看起来跟MVP好像没啥差异,其实区别仍是挺大的,在MVP中,View和presenter要相互持有,方便调用对方,而在MVP中,View和ViewModel经过Binding进行关联,他们以前的关联处理经过DataBinding
完成。
MVVM与DataBinding的关系用一句话表述就是,MVVM是一种思想,DataBinding是谷歌推出的方便实现MVVM的工具
。
在google推出DataBinding以前,由于xml layout功能较弱,想实现MVVM很是困难。而DataBinding的出现可让咱们很方便的实现MVVM。
看起来MVVM很好的解决了MVC和MVP的不足,可是因为数据和视图的双向绑定,致使出现问题时不太好定位来源
,有可能数据问题致使,也有可能业务逻辑中对视图属性的修改致使。若是项目中打算用MVVM的话能够考虑使用官方的架构组件ViewModel、LiveData、DataBinding
去实现MVVM。
关于ViewModel、LiveData、DataBindin这些类或框架的使用,由于涉及到的内容比较多,这里不详细介绍。
前面在介绍MVC、MVP、MVVM时并无去详细列出他们的优缺点,主要缘由是:关于架构,设计,模块化等等,它们的优缺点没有绝对的,主要看实现者如何去作
好比在mvp中咱们要实现根据业务逻辑和页面逻辑作不少Present和View的具体实现,若是这些case太多,会致使代码的可读性变差。可是经过引入contract契约类,会让业务逻辑变得清晰许多。所以不论是用哪一种设计模式,只要运用得当,均可以达到想要的结果。
若是非要说怎么选的话,通常的建议以下:
项目简单
,没什么复杂性,将来改动也不大的话,那就不要用设计模式或者架构方法,只须要将每一个模块封装好,方便调用便可,不要为了使用设计模式或架构方法而使用。展现型
的app,绝大多数业务逻辑都在后端,app主要功能就是展现数据,交互等,建议使用mvvm
。业务逻辑
app,使用mvp或者mvvm均可。MVC
而后在此基础上慢慢挖掘改进。最后你可能发现,改进的最终结果可能就变成了mvp,mvvm。2019-5-9