一: MVP是什么?
随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。
在MVP模式里通常包含4个要素:
1. View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
2. View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;
3. Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
4. Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
二:MVP同MVC的区别
其实最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的。
三:MVP优缺点
优点:
(1)降低耦合度
(2)模块职责划分明显
(3)利于测试驱动开发
(4)代码复用
(5)隐藏数据
(6)代码灵活性
缺点:
由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它 与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了
四、为什么使用MVP模式
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界 面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以 致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presneter)中时,Activity其实就是MVP模式中View,它 负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由 Presenter处理).
另外,回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或 真机 上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效 率的降 低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明了什么?说明 我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。
五:MVP使用实例
view接口:
public interface MainView{
void showProgress();
void hideProgress();
void setIpText(String text);
}
Presenter接口:
public interface MainPresenter extends BasePresenter {
void getIpInfo(String ip);
}
Presenter实现类:
public class MainPresenterImpl extends BasePresenterImpl implements MainPresenter { private static final String TAG = "TAG"; private MainView mMainView; public MainPresenterImpl(MainView mainView) { mMainView = mainView; } @Override public void getIpInfo(String ip) { if (TextUtils.isEmpty(ip)) { Toast.makeText(BaseApplication.getContext(), R.string.input_tip_ip, Toast.LENGTH_SHORT).show(); return; } mMainView.setIpText(""); mMainView.showProgress(); ServiceManager.getInstance().getApiService().getIpInfo(ip) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<GetIpInfoResponse>() { @Override public void onCompleted() { mMainView.hideProgress(); } @Override public void onError(Throwable e) { Log.e(TAG, e.getMessage(), e); mMainView.hideProgress(); mMainView.setIpText(BaseApplication.getContext().getString(R.string.network_error)); } @Override public void onNext(GetIpInfoResponse getIpInfoResponse) { mMainView.setIpText(getIpInfoResponse.data.country + " " + getIpInfoResponse.data.area); } }); } }
MainActivity:
public class MainActivity extends AppCompatActivity implements MainView {
private EditText mEtIp;
private TextView mTvContent;
private ProgressBar mProgressBar;
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mEtIp = (EditText) findViewById(R.id.et_ip);
mTvContent = (TextView) findViewById(R.id.tv_content);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mMainPresenter = new MainPresenterImpl(this);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mMainPresenter.getIpInfo(mEtIp.getText().toString());
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void setIpText(String text) {
mTvContent.setText(text);
}
}