1.MVC架构的特点
在Android当中,相比传统的MVC设计模式,View层(视图层)对应的是Android当中的布局文件。而布局文件是用XML格式来写的。但是这个XML布局文件它并不像Java Web端那么强大,能做的事情非常有限。而Controller所对应的Activity这个类,它不仅要处理业务逻辑,同时它也要处理Android当中的一些操作UI的功能。我们在实际项目开发过程中,有很多UI操作的逻辑写在Activity当中。这就导致Controller层中充斥了很多View层应当做的内容,从而使得Controller层非常冗余厚重。为了改善这一点,我们引入了MVP的架构模式。
2.MVP定义
<1>M:依然是业务逻辑和实体模型。
<2>V:对应于Activity,负责View的绘制以及与用户交互。
<3>P:负责完成VIew与Model间的交互。
3.MVP的优点
相较于MVC,MVP加入了Presenter层,我们便可以把之前Activity中作为Controller层的内容抽出来放入Presenter层,Activity则可以彻底加入View层了。这样我们也就完全切断了View层与Model层之间的联系。Activity与XML作为View层只对如何操作UI、渲染数据到界面上关心,而Model层则只关心如何获取数据源。至于数据源的二次处理以及数据注入UI则由Presenter关心,彻底切断数据与界面渲染。
4.MVP实例讲解
根据实例我们学习一下MVP,顺手搭建一套MVP的框架
首先是View层,我们先定义一个接口
/**
* View层接口基类
*/
public interface MvpBaseIView {
}
这个接口基类其实是为了一会儿多个类的关联使用,里面是没有代码的。
接着是Activity的基类
/**
* View层Activity基类,在子类中实现MvpBaseIView子类
*/
public abstract class MvpBaseActivity<T extends MvpBaseIPresenter> extends AppCompatActivity{
protected Bundle savedInstanceState;
protected T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在界面未初始化之前调用的初始化窗口
initWindows();
if (initArgs(getIntent().getExtras())) {
this.savedInstanceState = savedInstanceState;
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(getContentLayoutId());
initWidget();
mPresenter = getPresenterImpl();
initData();
} else {
finish();
}
}
/**
* 初始化窗口
*/
protected void initWindows() {
}
/**
* 初始化相关参数
*
* @param bundle 参数bundle
* @return 如果参数正确返回true, 错误返回false
*/
protected boolean initArgs(Bundle bundle) {
return true;
}
/**
* 得到当前界面的资源文件Id
*
* @return 资源文件Id
*/
protected abstract int getContentLayoutId();
/**
* 初始化控件
*/
protected void initWidget() {
}
/**
* 初始化数据
*/
protected void initData() {
}
protected abstract T getPresenterImpl();
}
这样View层的两个基类就都有了。
接下来是Model层的两个
Model层回调接口基类
public interface MvpBaseCallBack {
}
Model层接口基类
public interface MvpBaseIModel {
}
Model基类
public class MvpBaseModel<T extends MvpBaseCallBack> {
protected T callback;
protected Context context;
public MvpBaseModel(Context context, T callback) {
this.context = context;
this.callback = callback;
}
}
最后就是我们的Presenter层
Presenter接口基类
/**
* Presenter层接口基类
*/
public interface MvpBaseIPresenter {
}
Presenter基类
public abstract class MvpBasePresenter<T extends MvpBaseIModel,U extends MvpBaseIView> {
protected T mModel;
protected U iView;
public MvpBasePresenter(Context context,U mView) {
mModel = getModelImp();
iView = mView;
}
protected abstract T getModelImp();
}
这样基类就完成了。我们可以看到,Activity中持有Presenter的对象,而Presenter中又持有IView和Model的对象。这样三者就联系在了一起。但是Activity和Model之间却没有直接的关联。
接下来是我们对于基类的使用。
首先先上一个实体类,模拟网络请求返回的实体
public class Entity {
private String str;
private int hide;
public Entity(String str, int hide) {
this.str = str;
this.hide = hide;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public int getHide() {
return hide;
}
public void setHide(int hide) {
this.hide = hide;
}
}
接着就是我们的View层两大件
/**
* View层视图接口
*/
public interface DemoIView extends MvpBaseIView {
void showText(String str);
void showView();
void hideView();
String getXText();
}
这里可以看到View层接口我定义了四个动作,分别是显隐控件和设置获取TextView上的文字,这个要根据具体页面设置。
/**
* View层视图实现类
*/
public class DemoActivity extends MvpBaseActivity<DemoIPresenter> implements DemoIView{
private TextView tv_center;
private View view_gang;
@Override
protected int getContentLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initWidget() {
tv_center = findViewById(R.id.tv_center);
view_gang = findViewById(R.id.view_gang);
}
@Override
protected void initData() {
mPresenter.getData();
}
@Override
protected DemoIPresenter getPresenterImpl() {
return new DemoPresenter(this,this);
}
@Override
public void showText(String str) {
tv_center.setText(str);
}
@Override
public void showView() {
view_gang.setVisibility(View.VISIBLE);
}
@Override
public void hideView() {
view_gang.setVisibility(View.GONE);
}
@Override
public String getXText() {
return tv_center.getText().toString().trim();
}
}
Activity实现了IView的接口,并且实现了里面的接口方法,将定义的动作表现在了界面上。这里注意,我们在getPresenterImpl中返回了Presenter类的实例,并且该Activity还实现了IView接口。
下来便是我们的Model层
public interface DemoCallback extends MvpBaseCallBack {
void success(Entity entity);
void failure();
}
Callback中规中矩,还是成功和失败两个接口。
接着是Model接口类
public interface DemoIModel extends MvpBaseIModel {
void getData(String strEnter);
}
Model实现类
public class DemoModel extends MvpBaseModel<DemoCallback> implements DemoIModel {
public DemoModel(Context context, DemoCallback callback) {
super(context, callback);
}
@Override
public void getData(String strEnter) {
try {
new Thread().sleep(2000);
if (strEnter.equals("11111")){
//成功
callback.success(new Entity("xixixxi",0));
}else{
//失败
callback.failure();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Model类中我们在getData方法中模拟了一个网络请求,并进行了回调。
这样我们的Model层也编写完成了。
下来就是Presenter层了。
我们先进行接口设计
public interface DemoIPresenter extends MvpBaseIPresenter {
void getData();
}
由于我们只有一个获取界面数据的需求,所以这里只定义了一个接口方法
public class DemoPresenter extends MvpBasePresenter<DemoIModel,DemoIView>
implements DemoIPresenter,DemoCallback{
private Context mContext;
public DemoPresenter(Context context, DemoIView mView) {
super(context, mView);
mContext = context;
}
@Override
protected DemoIModel getModelImp() {
return new DemoModel(mContext,this);
}
@Override
public void success(Entity entity) {
iView.showText(entity.getStr());
if (entity.getHide() == 0) {
iView.hideView();
}else{
iView.showView();
}
}
@Override
public void failure() {
ToastUtil.toastWord(mContext,"错误");
}
@Override
public void getData() {
mModel.getData(iView.getXText());
}
}
在Presenter的实现类中,一方面是刚才我们定义的接口方法getData中调用了Model层的方法。另一方面是我们在回调中调用了IView接口中方法对界面进行了操作。当然还有在getModelImp中返回我们创建的Model类实例。这就是一套完整的MVP设计模式。我个人觉得MVP除了解耦,最大的魅力就是开发的不耽误功夫。我们可以先进行界面的开发,将界面可能需要的工作提前都设置好,比如给TextView设置文本,从Edittext获取值,根据变量显隐控件,按钮需要的点击事件等,都可以提前设置好对应的方法。接着等接口出来我们就可以很快对接接口,并将数据设置进去。不影响之前的界面,方便快捷,思路清晰。