浅谈MVP架构的实现方式(架构思想)
2016-07-15 18:32
465 查看
MVP模式
- 谈起MVC模式可能大家都耳熟能详,最开始多用于web应用的开发,后在移动开发的过程中也引入了MVC,但是很多公司的项目在使用MVC的时候并没有很好的将三层解耦,很多的数据请求操作仍是在Activity里面执行,造成很多代码的可维护性仍然不高。
- MVP模式的使用大大降低了mode和view之间的耦合度,方便应用的扩展。MVP结构中View和Model都通过Presenter来进行解耦。
项目主要实现的功能是展示一个列表数据,用不同的方式展示,一种是ListView的方式,一种是GridView的方式,项目结构通过对扩展开放对修改关闭,在不修改源代码的情况下修改展示方式。
上面的是最基本的实现方式,接下来使用MVP来实现,在MVP模式中有三个角色,Model、View、Presenter,先从简单的Model开始,Model中所要做的工作就是获取数据,在实际开发中,最好有一个BaseModel的接口定义一些抽象方法,Demo中为了演示是从本地获取的数据,在实际的开发中则为网络数据加载。
上述讲述了model的定义,model层完成之后来进行View层的编码,由于在MVP模式中view只需要来进行显示或者事件分发操作,所以可以先抽象出一个View接口用来定义一些需要实现的方法(该接口需要在Avtivity中去实现。此处为什么需要定义View接口,因为在Presenter中需要传递一个View来供Presenter获取数据之后回调具体的操作,后面会讲到)
因为项目实现的是一个数据列表,所以该页面需要的操作就是加载数据的提示和显示数据。
下一步是定义Avtivity来实现这个View
实现了View和Model下面就要实现Presenter,作为MVP中最重要的角色,Presenter作为中间人的最主要的任务就是请求数据,通知View显示数据,下面来看看Presenter的代码:
这里大家可以明白为什么在写View的时候需要一个接口了吧,在这里面我们可以使用里氏代换,然后调用父类的方法,子类的实现。到目前为止已经用MVP实现了这个功能。大家整理一下思路然后继续下面的东西
现在需要换一种方式来实现这个功能,上面的是使用一个ListView,接下来将使用GridView,可能大家会说直接写一个布局吧Activity中的ListView一更换就行了,确实是可以实现,但是如果也无需求又需要修改回ListView呢,难道我们就一直改这个,并且编程的思想要求我们扩展,所以现在用MVP实现GridView,由于数据获取方式和Avtivity要做的工作不变,所以Model和View接口不需要更改,只需要重新定义一个Activity就行。
修改的东西就是更换了一个GridView,但是对于我们开发者来说这样更利于代码维护,有利于扩展。
ok,接下来我们考虑一个问题,就是如果我们的数据获取方式变了呢,如果开始用的是retrofit,但是技术经理非得要求用OkHttp呢,根据上面的思路,我们需要重新定义一个Model
这样我们在presenter中只需要修改一下调用的方法即可达到不同方式的获取数据。
接下来大家需要考虑一个问题,既然说架构,需要考虑的东西当然很多,举一个例子:加入我们在Avtivity中进行网络请求,但是在没有请求完成之前该页面被销毁了,用户点击了返回,那么由于Presenter持有了该VIew对象,所以造成了内存不能回收activity占用的内存,造成了内存泄漏问题,如何来解决这样的问题,这里我使用WeakReference。先梳理一下思路:
Presenter持有View的引用,所以如果想解决内存泄漏,该持有方式应该为弱引用,既然每个Presenter都需要时弱引用,所以这里我用一个抽象类来实现
用泛型是因为我们不能确定用户是那一个View。
有了Presenter我们需要考虑activity了,在activity销毁的时候我们需要释放弱引用,在创建activity的时候我们需要绑定View到Presenter,所以BaseActivity的代码为:
ok 到此已经都实现完了,有了BaseActivity和BasePresenter之后可能上面的activity和presenter都有需要修改的地方,这里代码就不再修改了,该源码已经上传到了Github。如果有什么问题大家可以向我提出疑问。本人QQ:2592522521
项目源码地址:https://github.com/zhangXiaolizi/ZglMVP
- 谈起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
相关文章推荐
- ASP.NET小结之MVC, MVP, MVVM比较以及区别(一)
- 浅析MVP模式中V-P交互问题及案例分享
- 完整的Android MVP开发之旅
- 详解Android MVP开发模式
- ASP.NET小结之MVC, MVP, MVVM比较以及区别(二)
- iOS 5 cocos2d 游戏开发 3ff8 实战:第4章 你的第一个游戏
- 很难吗?教你三天精仿一个跨平台的微信
- android webview与js交互时遇到的问题解决
- 7月国外最新技术文章翻译汇总(IT技术)
- windows phone 8 的新特性
- 使用Html5开发Android和iOS应用:HBuilder、Html5Plus、MUI
- 2016年Bus365移动部门的年会节目-三句半
- Titanium 快速入门
- Titanium 用户界面之布局结构及核心代码块
- Android中的手势交互
- 感受Android系统的魅力
- Android像素转换的研究(一)
- 关于选择移动开发平台(android,ios,wp7)的一些看法