Android中MVP设计使用
2016-03-06 20:23
507 查看
什么是MVVP
现在,设计一款新的APP,对于Android而言,至少都是什么
Material Design,然后又是什么
MVP什么什么的。在
MVC,
MVVM都还没有弄明白的时候,又出来了新的
MVP,是不是有种淡淡的忧伤??
那么问题来了,到底什么是MVP呢?
MVP是从经典的模式
MVC演变而来,它们的基本思想有相通的地方:
Controller/Presenter负责逻辑的处理,
Model提供数据,
View负责显示。作为一种新的模式,
MVP与
MVC有着一个重大的区别:在
MVP中
View并不直接使用
Model,它们之间的通信是通过
Presenter(
MVC中的
Controller)来进行的,所有的交互都发生在
Presenter内部,而在
MVC中
View会直接从
Model中读取数据而不是通过
Controller。
在
MVC里,
View是可以直接访问
Model的!从而,
View里会包含
Model信息,不可避免的还要包括一些业务逻辑。 在
MVC模型里,更关注的
Model的不变,而同时有多个对
Model的不同显示,及
View。所以,在
MVC模型里,
Model不依赖于
View,但是
View是依赖于
Model的。不仅如此,因为有一些业务逻辑在
View里实现了,导致要更改
View也是比较困难的,至少那些业务逻辑是无法重用的。
以上是百度百科对
MVP设计优势的一些解释。结合之前的
MVC设计模式的话,其实
MVP就是更加简化了
Activity等
View的职责,让他们交出了数据管理等权利,而是纯粹的经行UI相关的操作!!(比如说控制
View的显示和隐藏,数据的相关展现。)
MVP的好处
、
根据上面两个图示可以看出,
MVP和
MVC的区别在于功能模块访问的权限,在
MVP中,
View层和
Mode数据层是不能直接有联系的。而是必须通过
Presenter层来完成一个统一的调度,
Presenter充当了
View层和
Mode层的中间桥梁,这样的好处就是实现了View和数据业务的完全解耦,是的逻辑层次更加分明,利于代码的维护!这个也满足设计中要求的单一职责原则。
MVP设计示例
大概说完了好处,那么就要说说刚入手MVP设计时的一些感觉不爽的地方,采用
MVP设计之后,整体的项目层级大概就是酱紫啦!
data就是
Model层相关的封装,包括
api请求等等,
presenters就是管理
View和
Model的中间层,
view就是
Activity相关的封装。
可以看到在
presenter和
view中都有相关的接口,在
MVP设计中,其精髓就在于面向接口编程,不依赖与具体实现。
具体来讲就是通过
View的相关接口定义出
view具体实现(
Activity)的一些基本方法,比如说(
onShowLoading(),
onShowDialog()),在相关的
presenter接口中也需要定义一系列的行为事件或者就叫抽象方法嘛!比如说(
onLoginButuonClick(),
onItemClicked(int position))这些都是需要去适应的,使用
MVP的模式编写代码,就要求我们更加注重于抽象,怎么解释呢,还是比如说登陆页面,一个输入用户名一个输入密码,一个登陆按钮。
大致流程就是点击登陆按钮,显示loading页面,获取两个输入框的文本内容,经行相关判断和检查(本地
trim()去空格,提交服务器check是否OK),关闭loading页面,然后根据返回内容做相关提示:登陆成功,跳转相关页面,登陆失败。。
采用MVP设计模式处理这些问题那么就需要如下进行:
*
Model层应该封装用户名和密码(
UserInfoBean)等,完成相关的api(
checkUserInfo(UserInfoBean bean))请求。
*
View层应该有
showLoading();
closeLoading();
showErrorView(String msg);
showHomeView();
getUserName();
getUserPasswrod();
*
Presenter层应该有(
onLoginButtonClick():响应
View登陆按钮的点击 )
OK,那么接下这三者到底怎么互相协调经行相关操作呢?
首先
LoginActivity我们的登陆页面,实现View的接口,依然是在
onCreate()里面初始化相关的
View。
在
Presenter和
View的交互中,在
View的初始化的时候
new出对应的
Presenter。并通过
Presenter的构造函数将view的引用传入
Presenter的具体实现类中。
final LoginPresenter presenter = new LoginPresenterImpl(this);
在
PresenterImpl的中:
private final LoginView view; private final Activity activity; public LoginPresenterImpl(LoginView view) { this.view = view; this.activity = (Activity) view; }
这样就完成了
Activity和
Presenter的交互回调了。
在
Activity的点击事件中:
Button mSignInButton = (Button) findViewById(R.id.email_sign_in_button); mSignInButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { presenter.onLoginClick(); } });
通过
presenter.onLoginClick(),将相关的处理逻辑调度给对应的
Presenter来处理了!
而在
Presenter的具体实现类
LoginPresenterImpl中:
@Override public void onLoginClick() { // Reset errors. view.setPasswordError(null); view.setUserNameError(null); // Store values at the time of the login attempt. String password = view.getPassWord(); String userName = view.getUserName(); // Check for a valid email address. if (TextUtils.isEmpty(userName)) { view.setUserNameError(activity.getString(R.string.error_field_required)); view.setUserNameFocus(); return; } // Check for a valid password, if the user entered one. if (TextUtils.isEmpty(password) ||!isPasswordValid(password)) { view.setPasswordError(activity.getString(R.string.error_invalid_password)); view.setPasswordFocus(); return; } UserLoginTask mLoginTask = new UserLoginTask(this, userName, password); if (!isRun) { mLoginTask.execute((Void) null); } }
可以看到,在
LoginPresenterImpl中,持有的
View的引用其实就是对应的LogingActivity
,在这里,先是调用view.setPasswordError(null),重置相关的错误提示,然后通过
String password = view.getPassWord()来获取对应的密码输入。接下来就是密码的非空判断,如果异常,那么就调用
view.setUserNameError(String msg),来通知
Activity相关的错误信息。最后如果没有问题的话,那么就开启子线程去走网络请求。
在
Model层中,定义的
LoginTask的接口:
public interface LoginTask { void onLoginSuccess(); void onLoginFailed(String msg); void onLoginCancelled(); void onLoginTaskStart();
}
这里直接让对应的
Presenter实现该接口,也是通过相关的接口,处理对应的逻辑!
比如说:
@Override public void onLoginCancelled() { view.showProgress(false); isRun = false; } @Override public void onLoginTaskStart() { isRun = true; view.showProgress(true); }
这分别是任务开始和取消的情况。
最后总结起来就是
view定义出对应的
Activity的相关行为(显现进度条、隐藏对话框等。)
Presenter定义出对应的处理行为动作(
Button的点击事件,滑动事件其实还有生命周期相关的比如
onResume()、
onDestroy()还有api请求的相关方法。)最后就保证
Presenter拥有绝对的控制和调动权,
Model层的相关结果通过
Presenter层调度之后调用
View的相关方法传递给具体的
View实现类,从而保证
View层和
Model层的完全解耦。
一些思考
1、View层的一些小动作也一定要报告给Presenter吗?比如说我在点击某个
Button之后要让某个
widget隐藏了!!??
如果真的就是很简单的如上面的这种情况,那么
View层可以自己处理这情况,但是其实一般都没有这么简单纯粹的情况,就像在公司做事儿一样,要养成向领导汇报的好习惯,可能很小的事情或者你觉得领导很忙这种事情完全可以不请示他,那么可要小心了,一旦这个事情变大,领导追究责任时。。呵呵哒!!
2、
View和
Presenter的对应关系?是一对一?是多对一?还是一对多?
就我目前的理解,我觉得
View和
Presenter应该是一对一的关系,如果对应的
View太复杂,业务逻辑层次太多,那么还可以定义多个
Presenter,这样
View和
Presenter就是一对多的关系了。我觉得不应该存在
View和
Presenter多对一的情况!这样必然要在Presenter里面定义很多tag,来区分具体的View!这样并不好!!
3、采用
MVP时不知道对应的
View和
Presenter应该定义那些抽象的方法??
这个问题其实也并不是问题,不要就是在那里空想,其实你把层次分好了,缺什么方法到需要的时候自然就知道了,这个时候再添加到对应的接口中就好了!也没有人能一次性就把所有的情况都考虑完全,所以多想多写,多积累相关经验。
4、采用
MVP会精简代码吗?
我一开始想的肯定能减少很多代码的。但是真的使用之后,你会发现,其实代码并没有减少,相反,可能还要多了!因为你要创建相关的抽象,直观的第一个体现就是一个项目的class变多了!但是它的层次结构和相关逻辑更加清晰了,有利必有弊吧!
写在最后
其实MVP就是更加强化了面向接口编程的一种具体体现。我们定义相关抽象的接口,具体的子类实现相关的方法实现相关的业务逻辑,对于习惯了一条线写下去的编码方式,刚开始的确会有点儿不习惯。因为撸着撸着,突然你要告诉自己,这里我们要交给
Presenter处理了。然后又到
Presenter里面,撸着撸着,我们这里要通知
View了!!
上面这个例子,只是一个入门的(很复杂的我其实也还没有写过呢!)。但是必须强调,这里的
Model层其实是比较弱化的。在实际开发中,数据库和网络请求更加强化,对应的
Model层就更复杂,比如说可能我们获取的相关
json数据,还要在
Presenter里面进行一些处理,然后才能分发给
View去更新相关界面!另外就是子线程和UI线程的频频切换,不仅是切换很不爽,而且相关的代码估计看着也让人很抓狂的!!
说了这么多,想说个撒呢?其实就是对应的网络请求,建议大家使用Retrofit 和 Rxjava
Rxjava,超级牛的异步请求解决方案,轻松解决你的子线程UI线程切换问题,链式调用,逻辑特别清楚。
而对于
Retrofit,
http网络请求的最佳实践,并且可以结合
Rxjava,写出来的代码那是一个飘逸!!!
相关下载
登陆示例知乎日报MVP+Retrofit
如有问题欢迎留言,喜欢记得点赞赞赞哟!!
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories