您的位置:首页 > 移动开发 > Android开发

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线程的频频切换,不仅是切换很不爽,而且相关的代码估计看着也让人很抓狂的!!

说了这么多,想说个撒呢?其实就是对应的网络请求,建议大家使用RetrofitRxjava

Rxjava
,超级牛的异步请求解决方案,轻松解决你的子线程UI线程切换问题,链式调用,逻辑特别清楚。

而对于
Retrofit
,
http
网络请求的最佳实践,并且可以结合
Rxjava
,写出来的代码那是一个飘逸!!!

相关下载

登陆示例

知乎日报MVP+Retrofit

如有问题欢迎留言,喜欢记得点赞赞赞哟!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android mvp 设计