MVP模式的概念
MVP能够有效地降低VIew复杂性,避免业务逻辑被塞进View中,使得View编程一个混乱的“大泥坑”。MVP模式会解除View与Model的耦合,同时又带来了良好的可扩展性、、可测试性,保证了系统的整洁性、灵活性。
MVP模式可以分离显示层和逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。
MVP的模型图
下面开始介绍MVP模式怎么写
- 首先要理解MVP模式的概念,因为要降低View与Model的耦合,所以大都是通过接口进行通信,先来看看项目的组成图。
可以看到主要分为3个包,一是contract,这个是创建View与Presenter的基类的;二是model,就是数据实体类;三是presenter,具体的presenter处理类,逻辑处理大都是放在这个类中,前面谈到理想中的MVP模式,是可以同一个presenter类运用于不同的activity或者fragment。这里只做了一个,处理邮件的presenter。
adapter,net中放的是网络请求的类,本项目用的网络请求框架是Retrofit网络请求框架,没学过的可以借鉴Retrofit的使用
2.先来看contract是怎么写的
BaseView
//泛类基View 作用是绑定view跟presenter
public interface BaseView<T> {
void setPresenter(T presenter);//在具体的presenter中绑定
}
MailInfoContract
/**
* 建立共同的契约类 方法的作用一致
* Created by prince70 on 2018/4/25.
*/
public interface MailInfoContract {
interface View extends BaseView<Presenter> {
void showLoading();//展示加载框
void dismissLoading();//隐藏加载框
void showMailInfo(MailModel.ContentBean bean);//将网络请求得到的邮件信息回调
String uid();//网络请求需要传参 uid
}
interface Presenter extends BasePresenter {
void loadMailInfo();//加载网络请求得到的邮件信息回调
}
}
3.接下来看看model,这个应该大家都懂,平时开发都用到
public class MailModel {
private int total;
private List<DataBean> data;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
public static class DataBean {
private int id;
private String body;
private String corpid;
private String title;
private String sendTime;
private String founder;
private int isdraft;
private String foundername;
private int accessorySum;
private int isread;
private int iscollect;
private Object typeName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getCorpid() {
return corpid;
}
public void setCorpid(String corpid) {
this.corpid = corpid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSendTime() {
return sendTime;
}
public void setSendTime(String sendTime) {
this.sendTime = sendTime;
}
public String getFounder() {
return founder;
}
public void setFounder(String founder) {
this.founder = founder;
}
public int getIsdraft() {
return isdraft;
}
public void setIsdraft(int isdraft) {
this.isdraft = isdraft;
}
public String getFoundername() {
return foundername;
}
public void setFoundername(String foundername) {
this.foundername = foundername;
}
public int getAccessorySum() {
return accessorySum;
}
public void setAccessorySum(int accessorySum) {
this.accessorySum = accessorySum;
}
public int getIsread() {
return isread;
}
public void setIsread(int isread) {
this.isread = isread;
}
public int getIscollect() {
return iscollect;
}
public void setIscollect(int iscollect) {
this.iscollect = iscollect;
}
public Object getTypeName() {
return typeName;
}
public void setTypeName(Object typeName) {
this.typeName = typeName;
}
}
}
}
这个类可以根据网络请求数据返回的json数据用GSON框架自动生成,非常便捷
* compile ‘com.squareup.retrofit2:converter-gson:2.3.0’*
3.presenter中放的就是主要的逻辑处理类MailInfoPresenter ,当然还有一个基类BasePresenter.废话不多说,直接看代码:
BasePresenter:
public interface BasePresenter {
/**
* 这里的start()方法就相当于约定了所有的Presenter的初始化操作都放在start()方法中;
*/
void start();
}
MailInfoPresenter:
public class MailInfoPresenter implements MailInfoContract.Presenter {
private static final String TAG = "MailInfoPresenter";
private MailInfoContract.View view;
public MailInfoPresenter(MailInfoContract.View view) {
this.view = view;
view.setPresenter(this);
}
@Override
public void start() {
loadMailInfo();
}
/**
* 从网络上获取数据
*/
@Override
public void loadMailInfo() {
String uid = view.uid();
view.showLoading();//接口请求前显示loading
Call<MailModel> call = MailUtil.getMails(uid);
call.enqueue(new Callback<MailModel>() {
@Override
public void onResponse(@NonNull Call<MailModel> call, @NonNull Response<MailModel> response) {
Log.e(TAG, "onResponse: " + call.request().toString());
MailModel body = response.body();
if (body != null) {
MailModel.ContentBean content = body.getContent();
view.showMailInfo(content);
}
view.dismissLoading();
}
@Override
public void onFailure(@NonNull Call<MailModel> call, @NonNull Throwable t) {
Log.e(TAG, "onFailure: " + call.request().toString());
view.dismissLoading();
}
});
}
}
可以看到所有的数据请求耗时操作都是放在MailInfoPresenter中处理,且与view建立关联,activity就主要负责显示的任务就好了
4.最后我们来看MainActivty中的代码
public class MainActivity extends AppCompatActivity implements MailInfoContract.View {
private static final String TAG = "MainActivity";
private ListView lv_mail;
private List<String> mails = new ArrayList<>();
private MailInfoContract.Presenter presenter;
private Dialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_mail = findViewById(R.id.lv_mail);
new MailInfoPresenter(this);
presenter.start();
}
@Override
public void setPresenter(MailInfoContract.Presenter presenter) {
this.presenter = presenter;
}
@Override
public void showLoading() {
mDialog = new ProgressDialog(this);
mDialog.setTitle("正在加载");
mDialog.show();
}
@Override
public void dismissLoading() {
mDialog.dismiss();
}
@Override
public void showMailInfo(MailModel.ContentBean bean) {
if (bean != null) {
int total = bean.getTotal();//这个数值代表请求的数据的条数,因为这个接口有上百条数据,这里只取前十条做测试
List<MailModel.ContentBean.DataBean> data = bean.getData();
for (int i = 0; i < 10; i++) {
String body = data.get(i).getBody();
mails.add(body);
}
Log.e(TAG, "showMailInfo: " + mails);
}
lv_mail.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mails));
}
@Override
public String uid() {
return "xuanhu";//传递参数
}
}
可以看到MainActivity实现了View的接口,绑定presenter,接口的通信就实现了。至此,一个简单的MVP模式项目就完成了,源码就到github地址
如有错误,还望不吝指正
仍可能存在的问题
综上所述,MVP有很多优点,例如易于维护,易于测试,松耦合、复用性高,健壮稳定,易于扩展等。但是,由于Presenter经常性地需要执行一些耗时操作,例如,我们上述示例的请求网络数据,而Presenter持有了MainActivity的强引用,如果在请求结束之前activity被销毁了,那么由于网络请求还没有返回,导致Presenter一直持有MainActivity对象,使得MainActivity对象无法被回收,此时就发生内存泄漏。
解决办法是通过弱引用和Activity、Fragment的生命周期来解决这个问题。
详情可参考《Android源码设计模式解析与实战》,当然你能够自己实现就最好