您的位置:首页 > 运维架构 > 网站架构

浅谈MVP架构的实现方式(架构思想)

2016-07-15 18:32 465 查看
MVP模式

- 谈起MVC模式可能大家都耳熟能详,最开始多用于web应用的开发,后在移动开发的过程中也引入了MVC,但是很多公司的项目在使用MVC的时候并没有很好的将三层解耦,很多的数据请求操作仍是在Activity里面执行,造成很多代码的可维护性仍然不高。

- MVP模式的使用大大降低了mode和view之间的耦合度,方便应用的扩展。MVP结构中View和Model都通过Presenter来进行解耦。





项目主要实现的功能是展示一个列表数据,用不同的方式展示,一种是ListView的方式,一种是GridView的方式,项目结构通过对扩展开放对修改关闭,在不修改源代码的情况下修改展示方式。



@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new AvatarListAdapter(this, list));

}


上面的是最基本的实现方式,接下来使用MVP来实现,在MVP模式中有三个角色,Model、View、Presenter,先从简单的Model开始,Model中所要做的工作就是获取数据,在实际开发中,最好有一个BaseModel的接口定义一些抽象方法,Demo中为了演示是从本地获取的数据,在实际的开发中则为网络数据加载。

BaseModel的代码:
public interface BaseModel
{
//加载数据的方法,因为没有返回值所以在数据获取到之后需要回调
void loadData(ResultCallBack callBack);

/**
* 加载完数据之后回调的函数
*/
interface ResultCallBack
{
void onResult(List result);
}
}

具体实现获取信息的model:
public class AvatarListModel implements BaseModel
{
@Override
public void loadData(ResultCallBack callBack)
{
List<AvatarBean> list = new ArrayList<AvatarBean>();
list.add(new AvatarBean(R.mipmap.image_1,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_2,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_3,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_4,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_5,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_6,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_7,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_8,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_9,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_10,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_11,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_12,"XiaoMing"));
//加载完成数据后回调处理
callBack.onResult(list);
}
}


上述讲述了model的定义,model层完成之后来进行View层的编码,由于在MVP模式中view只需要来进行显示或者事件分发操作,所以可以先抽象出一个View接口用来定义一些需要实现的方法(该接口需要在Avtivity中去实现。此处为什么需要定义View接口,因为在Presenter中需要传递一个View来供Presenter获取数据之后回调具体的操作,后面会讲到)

View接口的定义:
public interface AvatarListView
{
/**
* 显示加载弹窗
*/
void showProgress();

/**
* 获取数据之后显示数据
*/
void showData(List<AvatarBean> result);
}


因为项目实现的是一个数据列表,所以该页面需要的操作就是加载数据的提示和显示数据。

下一步是定义Avtivity来实现这个View

Activity的代码:
public class AvatarListActivity extends BaseActivity implements AvatarListView
{
private ListView listView;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);
AvatarListPresenter presenter = new AvatarListPresenter(this);
presenter.fetch();
}

/**
* 该方法是提示加载数据,实际开发中可能为一个加载动画
*/
@Override
public void showProgress()
{
Toast.makeText(this, "一大波美女即将到来", Toast.LENGTH_SHORT).show();
}

/**
* 该方法是在获取数据之后显示到适配器
* @param result 获取到的数据
*/
@Override
public void showData(List<AvatarBean> result)
{
listView.setAdapter(new AvatarListAdapter(this,result));
}

}


实现了View和Model下面就要实现Presenter,作为MVP中最重要的角色,Presenter作为中间人的最主要的任务就是请求数据,通知View显示数据,下面来看看Presenter的代码:

presenter的代码:
public class AvatarListPresenter
{
//Model层中的model,用来获取数据
private BaseModel model;
//View层的View,即实现了该接口的activity
private AvatarListView view;

/**
* Presenter从网络上夹在数据并且交由View处理
*/
public void fetch()
{
view.showProgress();
model = new AvatarListModel();
model.loadData(new AvatarListModel.ResultCallBack()
{
@Override
public void onResult(List result)
{
view.showData(result);
}
});
}
}


这里大家可以明白为什么在写View的时候需要一个接口了吧,在这里面我们可以使用里氏代换,然后调用父类的方法,子类的实现。到目前为止已经用MVP实现了这个功能。大家整理一下思路然后继续下面的东西

现在需要换一种方式来实现这个功能,上面的是使用一个ListView,接下来将使用GridView,可能大家会说直接写一个布局吧Activity中的ListView一更换就行了,确实是可以实现,但是如果也无需求又需要修改回ListView呢,难道我们就一直改这个,并且编程的思想要求我们扩展,所以现在用MVP实现GridView,由于数据获取方式和Avtivity要做的工作不变,所以Model和View接口不需要更改,只需要重新定义一个Activity就行。

GridView方式实现代码:
public class AvatarGridActivity extends BaseActivity<AvatarListView,AvatarListPresenter> implements AvatarListView
{
GridView gridView;
private List list;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_avatar_grid);

gridView = (GridView)findViewById(R.id.gridView);

gridView.setAdapter(new AvatarGridAdapter(this,list));
AvatarListPresenter presenter = new AvatarListPresenter(this);
presenter.fetch();

}

@Override
public void showProgress()
{
Toast.makeText(this, "数据加载中。。。", Toast.LENGTH_SHORT).show();
}

@Override
public void showData(List<AvatarBean> result)
{
gridView.setAdapter(new AvatarGridAdapter(this,result));
}
}


修改的东西就是更换了一个GridView,但是对于我们开发者来说这样更利于代码维护,有利于扩展。

ok,接下来我们考虑一个问题,就是如果我们的数据获取方式变了呢,如果开始用的是retrofit,但是技术经理非得要求用OkHttp呢,根据上面的思路,我们需要重新定义一个Model

public class AvatarNetWorkModel implements BaseModel
{
@Override
public void loadData(ResultCallBack callBack)
{
//线程休眠5秒钟
/*try
{
Thread.sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
}*/
//模拟网络请求数据
//模拟网络延时
SystemClock.sleep(5000);

List<AvatarBean> list = new ArrayList<AvatarBean>();
list.add(new AvatarBean(R.mipmap.image_1,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_2,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_3,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_4,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_5,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_6,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_7,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_8,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_9,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_10,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_11,"XiaoMing"));
list.add(new AvatarBean(R.mipmap.image_12,"XiaoMing"));
//加载完成数据后回调处理
callBack.onResult(list);
}
}


这样我们在presenter中只需要修改一下调用的方法即可达到不同方式的获取数据。

接下来大家需要考虑一个问题,既然说架构,需要考虑的东西当然很多,举一个例子:加入我们在Avtivity中进行网络请求,但是在没有请求完成之前该页面被销毁了,用户点击了返回,那么由于Presenter持有了该VIew对象,所以造成了内存不能回收activity占用的内存,造成了内存泄漏问题,如何来解决这样的问题,这里我使用WeakReference。先梳理一下思路:

Presenter持有View的引用,所以如果想解决内存泄漏,该持有方式应该为弱引用,既然每个Presenter都需要时弱引用,所以这里我用一个抽象类来实现

public abstract class BasePresenter<T>
{
protected WeakReference<T> mViewRef;

/**
* Presenter关联View,使用弱引用保证在View被销毁之后及时回收内存,避免内存泄漏
* @param view
*/
public void attachView(T view)
{
mViewRef = new WeakReference<T>(view);
}

/**
* 解除关联
*/
public void detachView()
{
if (mViewRef != null)
{
mViewRef.clear();
mViewRef = null;
}
}

protected T getView()
{
return mViewRef.get();
}

}


用泛型是因为我们不能确定用户是那一个View。

有了Presenter我们需要考虑activity了,在activity销毁的时候我们需要释放弱引用,在创建activity的时候我们需要绑定View到Presenter,所以BaseActivity的代码为:

public abstract class BaseActivity<V ,T extends BasePresenter<V>> extends Activity
{
protected T presenter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

presenter = createPresenter();

//将View关联到Presenter
presenter.attachView((V)this);
}

@Override
protected void onDestroy()
{
super.onDestroy();
//解除关联
presenter.detachView();
}

/**
* 获取每个页面中的需要的Presenter,用来获取数据并回调页面处理
* @return
*/
protected abstract T createPresenter();

}


ok 到此已经都实现完了,有了BaseActivity和BasePresenter之后可能上面的activity和presenter都有需要修改的地方,这里代码就不再修改了,该源码已经上传到了Github。如果有什么问题大家可以向我提出疑问。本人QQ:2592522521

项目源码地址:https://github.com/zhangXiaolizi/ZglMVP
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mvp 移动开发