Android 中 MVC 的简单理解https://blog.csdn.net/qq_17798399/article/details/95933628
MVP模式
相信大家对MVC都是比较熟悉了:M-Model-模型、V-View-视图、C-Controller-控制器,MVP作为MVC的演化版本,那么类似的MVP所对应的意义:M-Model-模型、V-View-视图、P-Presenter-表示器。 从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。而 MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控 View与Model之间的间接交互,MVP的结构图如下所示,对于这个图理解即可而不必限于其中的条条框框,毕竟在不同的场景下多少会有些出入的。在 Android中很重要的一点就是对UI的操作基本上需要异步进行也就是在MainThread中才能操作UI,所以对View与Model的切断分离是 合理的。此外Presenter与View、Model的交互使用接口定义交互操作可以进一步达到松耦合也可以通过接口更加方便地进行单元测试。
Model:业务逻辑和实体模型,用来操作实际的数据,包含Bean和Model的抽象接口来降低耦合。
View:就是Android中的视图,需要建立一个View的抽象接口View Interface。通过实现View的接口来实现View与Presenter的交互,从而降低耦合。对应于Activity,负责View的绘制与用户交互;
Presenter:View和Model的中间枢纽,处理和用户交互的逻辑。
为什么要用MVP?
其实我们日常开发中的Activity,Fragment和XML界面就相当于是一个 MVC 的架构模式,Activity中不仅要处理各种 UI 操作还要请求数据以及解析。
这种开发方式的缺点就是业务量大的时候一个Activity 文件分分钟飙到上千行代码,想要改一处业务逻辑光是去找就要费半天劲,而且有点地方逻辑处理是一样的无奈是不同的 Activity 就没办法很好的写成通用方法。
那 MVP 为啥好用呢?
MVP 模式将Activity 中的业务逻辑全部分离出来,让Activity 只做 UI 逻辑的处理,所有跟Android API无关的业务逻辑由 Presenter 层来完成。
将业务处理分离出来后最明显的好处就是管理方便,但是缺点就是增加了代码量。
MVP框架的搭建
MVP快速生成类的插件: https://github.com/githubwing/MVPHelper
下面演示下登陆的MVP实现方式:
(示例代码由开发项目中剥离到Demo中,登陆接口使用的是玩安卓的登陆API:http://www.wanandroid.com/blog/show/2)
首先,创建一个登陆的Contract:
public interface MainContract {
interface Model { }
interface View extends BaseView { }
interface Presenter { }
}
其次创建Presenter、Model、View 对应Contract中的接口;
public class MainPresenter implements MainContract.Presenter{}
public class MainModel implements MainContract.Model{}
public class MainActivity implements MainContract.View {}
完整的Contract:
public interface MainContract {
interface Model {
Flowable<BaseObjectBean<LoginBean>> login(String username, String password);
}
interface View extends BaseView {
@Override
void showLoading();
@Override
void hideLoading();
@Override
void onError(Throwable throwable);
void onSuccess(BaseObjectBean<LoginBean> bean);
}
interface Presenter {
/**
* 登陆
*
* @param username
* @param password
*/
void login(String username, String password);
}
}
在MainContract 中
Model接口 创建对应的联网请求的方法,将Presenter提交的字段放到联网请求中,发送给服务器
View 接口 创建在界面上显示加载中、取消加载以及登陆成功、失败的方法
Presenter 接口 创建 登陆的方法,以及需要提交的字段 (username、password)
MainModel的完整代码:
public class MainModel implements MainContract.Model {
@Override
public Flowable<BaseObjectBean<LoginBean>> login(String username, String password) {
return RetrofitClient.getInstance().getApi().login(username,password);
}
}
Model类实现MainContract.Model 接口中的 login(String username, String password)方法,将username、password放在联网请求中,进行请求服务器。
MainView 的完整代码:
public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
@BindView(R.id.et_username_login)
TextInputEditText etUsernameLogin;
@BindView(R.id.et_password_login)
TextInputEditText etPasswordLogin;
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void initView() {
mPresenter = new MainPresenter();
mPresenter.attachView(this);
}
/**
* @return 帐号
*/
private String getUsername() {
return etUsernameLogin.getText().toString().trim();
}
/**
* @return 密码
*/
private String getPassword() {
return etPasswordLogin.getText().toString().trim();
}
@Override
public void onSuccess(BaseObjectBean bean) {
Toast.makeText(this, bean.getErrorMsg(), Toast.LENGTH_SHORT).show();
}
@Override
public void showLoading() {
ProgressDialog.getInstance().show(this);
}
@Override
public void hideLoading() {
ProgressDialog.getInstance().dismiss();
}
@Override
public void onError(Throwable throwable) {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: add setContentView(...) invocation
ButterKnife.bind(this);
}
@OnClick(R.id.btn_signin_login)
public void onViewClicked() {
if (getUsername().isEmpty() || getPassword().isEmpty()) {
Toast.makeText(this, "帐号密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
mPresenter.login(getUsername(), getPassword());
}
}
MainActivity 中实现 MainContract.View中的方法 ,在实现的方法中,进行进度条加载、和登陆成功or失败的UI的展示:
@Override
void showLoading();
@Override
void hideLoading();
@Override
void onError(Throwable throwable);
void onSuccess(BaseObjectBean<LoginBean> bean);
MainPresenter 的完整代码:
public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
private MainContract.Model model;
public MainPresenter() {
model = new MainModel();
}
@Override
public void login(String username, String password) {
if (!isViewAttached()) {
return;
}
mView.showLoading();
model.login(username, password)
.compose(RxScheduler.<BaseObjectBean<LoginBean>>Flo_io_main())
.as(mView.<BaseObjectBean<LoginBean>>bindAutoDispose())
.subscribe(new Consumer<BaseObjectBean<LoginBean>>() {
@Override
public void accept(BaseObjectBean<LoginBean> bean) throws Exception {
mView.onSuccess(bean);
mView.hideLoading();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
mView.onError(throwable);
mView.hideLoading();
}
});
}
}
MainPresenter 实现MainContract.Presenter 接口中的 login(String username, String password) 方法
实例化Model,在MainPresenter login(String username, String password)方法中,调用model的网络请求,将username、password放在model的login()方法中,进行请求服务器。
请求服务器前 使用MainContract.View中的 mView.showLoading()方法,进行显示加载中;在成功失败的回调中,使用对应的方法,以及取消加载。
其中BasePresenter、BaseView 是对Presenter以及View进行的封装
BaseView类:
public interface BaseView {
/**
* 显示加载中
*/
void showLoading();
/**
* 隐藏加载
*/
void hideLoading();
/**
* 数据获取失败
* @param throwable
*/
void onError(Throwable throwable);
/**
* 绑定Android生命周期 防止RxJava内存泄漏
*
* @param <T>
* @return
*/
<T> AutoDisposeConverter<T> bindAutoDispose();
}
至于为什么不把onSuccess()方法也封装,是因为请求网络,服务器返回的值是不一样的,在Contract > View接口中根据bean类设置onSuccess()
BasePresenter类:
public class BasePresenter<V extends BaseView> {
protected V mView;
/**
* 绑定view,一般在初始化中调用该方法
*
* @param view view
*/
public void attachView(V view) {
this.mView = view;
}
/**
* 解除绑定view,一般在onDestroy中调用
*/
public void detachView() {
this.mView = null;
}
/**
* View是否绑定
*
* @return
*/
public boolean isViewAttached() {
return mView != null;
}
}