MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。
模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
视图(View):负责界面数据的展示,与用户进行交互;
主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。
如下图所示,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。
书面的说法就是为了解耦,为了可维护性,可扩展性。简而言之就是OCP开闭原则,对于扩展开放,对于修改关闭。
但是我更想用个人实战经验来说明架构设计在开发中的好处。初学android的时候觉得android很麻烦,要写很多的配置文件,加上自己也没有多么高深的架构设计能力,所以曾经写一个应用一个service达到了近1000行代码。对,你没听错,后期维护的时候很恶心人,我现在看到那个项目就想吐。这就是没有架构胡乱敲代码所导致的后果。还好google工程师在android里其实内置了很多的设计模式,所以我们的代码才没有更恶心下去。用了mvp架构设计后个人觉得非常的方便,代码风格发生了很大的变化,开始习惯使用泛型,开始习惯不写重复代码,并且开始自己封装一些简单的框架,抽出一些公共的方法。由于上面也讲了mvp具体是怎么回事,我就不多啰嗦了。
renyou.com.mvp.activity
renyou.com.mvp.model
renyou.com.mvp.view
renyou.com.mvp.presenter
renyou.com.mvp.bean
renyou.com.mvp.data
renyou.com.mvp.adapter
public class MvpActivity extends AppCompatActivity implements MvpView{
private ListView listview;
private MvpAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
initView();
new MvpPresenterV1(this).fetch();
}
private void initView() {
listview= (ListView) findViewById(R.id.lv_main);
}
@Override
public void showLoading() {
Toast.makeText(getApplicationContext(),"正在加载",Toast.LENGTH_LONG).show();
}
@Override
public void showDatas(Listdatas) {
mAdapter=new MvpAdapter(getApplicationContext(), datas);
listview.setAdapter(mAdapter);
}
}
MvpModel:model层接口
public interface MvpModel { void loadDatas(DataOnLoadingListener listener); interface DataOnLoadingListener{ void onComplete(Listdatas); }}
MvpModelImplV1:model层实现类
public class MvpModelImplV1 implements MvpModel { @Override public void loadDatas(DataOnLoadingListener listener) { Listdatas= DataUtil.getInstance().getData(); listener.onComplete(datas); }}
MvpView:view层接口
public interface MvpView { /** * 显示加载进度 */ void showLoading(); /** * 显示数据 * @param datas 要显示的数据 */ void showDatas(Listdatas);}
MvpPresenterV1:presenter类
public class MvpPresenterV1 { //view MvpView mvpView; //model MvpModel mvpModel=new MvpModelImplV1(); public MvpPresenterV1(MvpView mvpView){ this.mvpView=mvpView; } /** * bind view and model */ public void fetch(){ mvpView.showLoading(); if(mvpModel!=null){ mvpModel.loadDatas(new MvpModel.DataOnLoadingListener() { @Override public void onComplete(Listdatas) { mvpView.showDatas(datas); } }); } }}
Renyou:bean类
public class Renyou {
private String name;
public Renyou(String name){
this.name=name;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
}
DataUtil:加载数据类
public class DataUtil { private static DataUtil dataUtil=null; public static DataUtil getInstance(){ if(dataUtil==null){ dataUtil=new DataUtil(); } return dataUtil; } public ListgetData(){ List datas = new ArrayList<>(); for (int i = 0; i <20; i++) { Renyou renyou = new Renyou("" + i); datas.add(renyou); } return datas; }}
MvpAdapter:listview适配器
public class MvpAdapter extends BaseAdapter { private Context context; private List我相信有面向对象基础的开发者都可以看的懂上述代码,这里就不一一解释了。datas; public MvpAdapter(Context context,List datas){ this.cOntext=context; this.datas=datas; } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return datas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if(cOnvertView==null){ cOnvertView=View.inflate(context, R.layout.item, null); } ViewHolder holder=ViewHolder.getHolder(convertView); holder.tv.setText(datas.get(position).getName()); return convertView; } static class ViewHolder{ TextView tv; public ViewHolder(View convertView){ tv=(TextView) convertView.findViewById(R.id.id_num); } public static ViewHolder getHolder(View convertView){ ViewHolder holder=(ViewHolder) convertView.getTag(); if(holder==null){ holder = new ViewHolder(convertView); convertView.setTag(holder); } return holder; } }}
上述代码缺陷:
如图所示:activity/view会持有presenter层的引用,presenter会引用model层的引用。model加载的数据极有可能是网络数据,且极有可能是异步子线程加载的数据。加载这样的数据要耗时,一旦用户点击回退推出了当前的view,则view就被释放了,但是当model层加载数据完成后会回掉监听器,他会拿着view的引用去访问一块已经被释放了的view内存。这样是没有必要的,是浪费资源的,导致了内存的泄漏。
解决方案:
新增的类
MvpBaseActivity基类
public abstract class MvpBaseActivity>extends AppCompatActivity { protected T mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter=createPresenter(); mPresenter.attachView((V)this); } @Override protected void onDestroy() { super.onDestroy(); mPresenter.detachView(); } public abstract T createPresenter();}
BasePresenter基类presenter
public abstract class BasePresenter{ /** * 当内存不足时释放内存 */ protected WeakReference mViewRef; /** * bind presenter with view * @param view */ public void attachView(T view){ mViewRef=new WeakReference (view); } /** * detach view manager */ public void detachView(){ if(mViewRef!=null){ mViewRef.clear(); mViewRef=null; } } protected T getView(){ return mViewRef.get(); }}
MvpPresenterV1让它继承基类presenter
public class MvpPresenterV1 extends BasePresenter{ //model MvpModel mvpModel=new MvpModelImplV1(); /** * bind view and model */ public void fetch(){ getView().showLoading(); if(mvpModel!=null){ mvpModel.loadDatas(new MvpModel.DataOnLoadingListener() { @Override public void onComplete(List datas) { getView().showDatas(datas); } }); } }}
MvpActivity让他继承基类
public class MvpActivity extends MvpBaseActivityimplements MvpView{ private ListView listview; private MvpAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mvp); initView(); mPresenter.fetch(); } @Override public MvpPresenterV1 createPresenter() { return new MvpPresenterV1(); } private void initView() { listview= (ListView) findViewById(R.id.lv_main); } @Override public void showLoading() { Toast.makeText(getApplicationContext(),"正在加载...",Toast.LENGTH_LONG).show(); } @Override public void showDatas(List datas) { mAdapter=new MvpAdapter(getApplicationContext(), datas); listview.setAdapter(mAdapter); }}