android mvp 代码范例,Android MVP开发模式有案例和源码,反正我能看懂的MVP

丁先森 博客园

MVP 理论知识

在MVP 架构中跟MVC类似的是同样也分为三层。

Activity 和Fragment 视为View层,负责处理 UI。

Presenter 为业务处理层,既能调用UI逻辑,又能请求数据,该层为纯Java类,不涉及任何Android API。

Model 层中包含着具体的数据请求,数据源。

三层之间调用顺序为view->presenter->model,为了调用安全着想不可反向调用!不可跨级调用!

那Model 层如何反馈给Presenter 层的呢?Presenter 又是如何操控View 层呢?看图!

AAffA0nNPuCLAAAAAElFTkSuQmCC

图是我借来的

上图中说明了低层的不会直接给上一层做反馈,而是通过 View 、 Callback 为上级做出了反馈,这样就解决了请求数据与更新界面的异步操作。上图中 View 和 Callback 都是以接口的形式存在的,其中 View 是经典 MVP 架构中定义的,Callback 是我自己加的。

View 中定义了 Activity 的具体操作,主要是些将请求到的数据在界面中更新之类的。

Callback 中定义了请求数据时反馈的各种状态:成功、失败、异常等。

MVP模式的核心思想:

MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。

使用MVP的优点

分离了视图逻辑和业务逻辑,降低了耦合

Activity只处理生命周期的任务,代码变得更加简洁

视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性

Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试

把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM

其中最重要的有三点:

Activity 代码变得更加简洁

相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。

使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。

方便进行单元测试

一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……

MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。

避免 Activity 的内存泄露

Android APP 发生OOM的最大原因就是出现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak))。

Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收。

Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。

采用传统的MV模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM。

采用MVP模式,只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak。

说了这么多,没看懂?好吧,我自己都没看懂自己写的,我们还是直接看代码吧。

先看一下目录结构

AAffA0nNPuCLAAAAAElFTkSuQmCC

这里肯定会有人说,我去每次创建新功能,不能每次创建那么多类吧,那不得麻烦死,这里推荐一个AndroidStudio的插件,AndroidMVP,看一下这个插件能实现的功能

AAffA0nNPuCLAAAAAElFTkSuQmCC

图还是借来的,这个还是挺好用的,看完插件那就再看看代码实现的效果吧,毕竟能看到才知道实现了什么效果

看完效果图,来看看代码是怎么实现的

AAffA0nNPuCLAAAAAElFTkSuQmCC

倒着来IView--->activity--->IPresenter--->PresenterImpl--->IModel--->ModelImpl

主要看请求的数据吧

ITwoActivity

public interface ITwoAView {

//请求标记

int REQUEST_ONE = 0;

int REQUEST_TWO = 1;

int REQUEST_THREE = 2;

//响应标记

int RESPONSE_ONE = 0;

int RESPONSE_TWO = 1;

int RESPONSE_THREE = 2;

T request(int requestFlag);

void response(T response, int responseFlag);

String getToken();

void showToast(String msg);

}

大多数都是自动生成的,只需要你自己到时候需要什么参数自己添加一下就行

TwoActivity

/**

* 测试获取数据集合,网络请求拿到集合对象

*/

public class TwoActivity extends BaseMvpActivity implements ITwoAView {

private ITwoAPresenter mITwoAPresenter;

private Button btn_getdata;//请求数据按钮

private EditText et_token;//模拟参数

private ListView lv_data_list;//listView

private List jsonpuInfoEntityList;//商铺集合

private JsonDataBean jsonDataBean;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mITwoAPresenter = new TwoAPresenterImpl(this);

setContentView(R.layout.activity_two);

initViewBind();

}

private void initViewBind() {

btn_getdata = (Button) findViewById(R.id.btn_getdata);

et_token = (EditText) findViewById(R.id.et_token);

lv_data_list = (ListView) findViewById(R.id.lv_data_list);

btn_getdata.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

mITwoAPresenter.getData();

}

});

}

@Override

public T request(int requestFlag) {

return null;

}

@Override

public void response(T response, int responseFlag) {

/*拿到的总的对象*/

if (responseFlag == IMainAView.RESPONSE_ONE) {

jsonDataBean = (JsonDataBean) response;

Log.e("jsonDataBean", "返回的数据信息:" + jsonDataBean.getHome_shopline());

jsonpuInfoEntityList = jsonDataBean.getHome_shoplist();

PuListAdapter puListAdapter = new PuListAdapter(TwoActivity.this, jsonpuInfoEntityList);

lv_data_list.setAdapter(puListAdapter);

}

}

@Override

public String getToken() {

return et_token.getText().toString();

}

@Override

public void showToast(String msg) {

Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();

}

}

ITwoAPresenter

public class TwoAPresenterImpl implements ITwoAPresenter {

private ITwoAView mITwoAView;

private ITwoAModel mITwoAModel;

public TwoAPresenterImpl(ITwoAView aITwoAView) {

mITwoAView = aITwoAView;

mITwoAModel = new TwoAModelImpl();

}

@Override

public void getData() {

mITwoAModel.getData(mITwoAView.getToken(), new CallBack() {

@Override

public void onSuccess(Object response) {

mITwoAView.response(response, IMainAView.RESPONSE_ONE);

mITwoAView.showToast("数据请求成功");

}

@Override

public void onError(String t) {

mITwoAView.response(mITwoAModel, IMainAView.RESPONSE_TWO);

mITwoAView.showToast(t);

}

});

}

}

ITwoAModel

public interface ITwoAModel {

/*请求数据*/

void getData(String token, CallBack callBack);

}

TwoAModelImpl

public class TwoAModelImpl implements ITwoAModel {

JsonDataBean jsondatabean;

@Override

public void getData(String token, final CallBack callBack) {

/*进行网络请求,获取数据*/

// 方式二:使用静态方式创建并显示,这种进度条只能是圆条,设置title和Message提示内容

if (token.equals("")) {

} else {

RequestQueue mQueue = Volley.newRequestQueue(AppApplication.getmContext());

StringRequest stringRequest = new StringRequest(Request.Method.POST, "http://www.mockhttp.cn/mock/upzl-android-home2", new Response.Listener() {

@Override

public void onResponse(String s) {

Log.e("login", "-------获取到的idjson--------" + s.toString());

Log.e("login", "-------JSON.parseObject(json).data--------" + JSON.parseObject(s.toString()).getString("data"));

jsondatabean = JSON.parseObject(JSON.parseObject(s.toString()).getString("data"), JsonDataBean.class);

//成功之后,传递出jsondatabean

if (jsondatabean != null) {//获取到了数据

callBack.onSuccess(jsondatabean);

} else {

callBack.onError(s);//获取失败信息

}

}

}, new Response.ErrorListener() {

@Override

public void onErrorResponse(VolleyError volleyError) {

}

}) {

@Override

protected Map getParams() throws AuthFailureError {

Map map = new HashMap();

return map;

}

};

/*设置请求一次*/

stringRequest.setRetryPolicy(

new DefaultRetryPolicy(

500000,//默认超时时间,应设置一个稍微大点儿的,例如本处的500000

DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//默认最大尝试次数

DefaultRetryPolicy.DEFAULT_BACKOFF_MULT

)

);

mQueue.add(stringRequest);/*请求数据*/

}

}

}

这里的请求和解析json是用的Volley和fastjson

JsonDataBean,这里也是用插件自动生成的,GsonFormat,只要把后台给你返回的json字符放进去,自己生成实体类,AS开发还是可以的。

public class JsonDataBean {

public JsonDataBean() {

}

public JsonDataBean(String home_shopnewnum, String home_shopline, String home_people, List home_imgurl, List home_h5url, List home_news, List home_shoplist) {

this.home_shopnewnum = home_shopnewnum;

this.home_shopline = home_shopline;

this.home_people = home_people;

this.home_imgurl = home_imgurl;

this.home_h5url = home_h5url;

this.home_news = home_news;

this.home_shoplist = home_shoplist;

}

/**

* home_imgurl : [{"imgId":1,"imgUrl":"http://img4.duitang.com/uploads/item/201403/27/20140327114737_w3uA3.jpeg"},{"imgId":1,"imgUrl":"http://img4.duitang.com/uploads/item/201403/27/20140327114737_w3uA3.jpeg"},{"imgId":1,"imgUrl":"http://img4.duitang.com/uploads/item/201403/27/20140327114737_w3uA3.jpeg"},{"imgId":1,"imgUrl":"http://img4.duitang.com/uploads/item/201403/27/20140327114737_w3uA3.jpeg"}]

* home_h5url : [{"url":"暂定"}]

* home_news : [{"newsId":1,"newUrl":"http://192.168.1.197/web/upH5/consult.html?id=123&url=2"},{"newsId":1,"newUrl":"http://192.168.1.197/web/upH5/consult.html?id=123&url=2"},{"newsId":1,"newUrl":"http://192.168.1.197/web/upH5/consult.html?id=123&url=2"}]

* home_shoplist : [{"shopId":1,"shopImgUrl":"http://up.enterdesk.com/edpic_source/e2/37/f1/e237f1f737e24dc0dbd6030a22b72005.jpg","shopName":"朝阳-双井|100㎡","shopAddress":"广平门黄平路平米","shopTags":[{"tag":"随便四字"},{"tag":"临近地铁"},{"tag":"最多四字"}],"shopMonery":"8000","shopMoneryUnit":"元/月"},{"shopId":1,"shopImgUrl":"http://up.enterdesk.com/edpic_source/e2/37/f1/e237f1f737e24dc0dbd6030a22b72005.jpg","shopName":"朝阳-双井|100㎡","shopAddress":"广平门黄平路平米","shopTags":[{"tag":"随便四字"},{"tag":"临近地铁"},{"tag":"最多四字"}],"shopMonery":"5.5","shopMoneryUnit":"万/月"}]

* home_shopnewnum : 814

* home_shopline : 77847

* home_people : 20173

*/

private String home_shopnewnum;

private String home_shopline;

private String home_people;

private List home_imgurl;

private List home_h5url;

private List home_news;

private List home_shoplist;

public String getHome_shopnewnum() {

return home_shopnewnum;

}

public void setHome_shopnewnum(String home_shopnewnum) {

this.home_shopnewnum = home_shopnewnum;

}

public String getHome_shopline() {

return home_shopline;

}

public void setHome_shopline(String home_shopline) {

this.home_shopline = home_shopline;

}

public String getHome_people() {

return home_people;

}

public void setHome_people(String home_people) {

this.home_people = home_people;

}

public List getHome_imgurl() {

return home_imgurl;

}

public void setHome_imgurl(List home_imgurl) {

this.home_imgurl = home_imgurl;

}

public List getHome_h5url() {

return home_h5url;

}

public void setHome_h5url(List home_h5url) {

this.home_h5url = home_h5url;

}

public List getHome_news() {

return home_news;

}

public void setHome_news(List home_news) {

this.home_news = home_news;

}

public List getHome_shoplist() {

return home_shoplist;

}

public void setHome_shoplist(List home_shoplist) {

this.home_shoplist = home_shoplist;

}

public static class HomeImgurlBean {

/**

* imgId : 1

* imgUrl : http://img4.duitang.com/uploads/item/201403/27/20140327114737_w3uA3.jpeg

*/

private int imgId;

private String imgUrl;

public int getImgId() {

return imgId;

}

public void setImgId(int imgId) {

this.imgId = imgId;

}

public String getImgUrl() {

return imgUrl;

}

public void setImgUrl(String imgUrl) {

this.imgUrl = imgUrl;

}

}

public static class HomeH5urlBean {

/**

* url : 暂定

*/

private String url;

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

}

public static class HomeNewsBean {

/**

* newsId : 1

* newUrl : http://192.168.1.197/web/upH5/consult.html?id=123&url=2

*/

private int newsId;

private String newUrl;

private String newMsg;

public String getNewMsg() {

return newMsg;

}

public void setNewMsg(String newMsg) {

this.newMsg = newMsg;

}

public int getNewsId() {

return newsId;

}

public void setNewsId(int newsId) {

this.newsId = newsId;

}

public String getNewUrl() {

return newUrl;

}

public void setNewUrl(String newUrl) {

this.newUrl = newUrl;

}

}

public static class HomeShoplistBean {

/**

* shopId : 1

* shopImgUrl : http://up.enterdesk.com/edpic_source/e2/37/f1/e237f1f737e24dc0dbd6030a22b72005.jpg

* shopName : 朝阳-双井|100㎡

* shopAddress : 广平门黄平路平米

* shopTags : [{"tag":"随便四字"},{"tag":"临近地铁"},{"tag":"最多四字"}]

* shopMonery : 8000

* shopMoneryUnit : 元/月

*/

private int shopId;

private String shopImgUrl;

private String shopName;

private String shopAddress;

private String shopMonery;

private String shopMoneryUnit;

private List shopTags;

public int getShopId() {

return shopId;

}

public void setShopId(int shopId) {

this.shopId = shopId;

}

public String getShopImgUrl() {

return shopImgUrl;

}

public void setShopImgUrl(String shopImgUrl) {

this.shopImgUrl = shopImgUrl;

}

public String getShopName() {

return shopName;

}

public void setShopName(String shopName) {

this.shopName = shopName;

}

public String getShopAddress() {

return shopAddress;

}

public void setShopAddress(String shopAddress) {

this.shopAddress = shopAddress;

}

public String getShopMonery() {

return shopMonery;

}

public void setShopMonery(String shopMonery) {

this.shopMonery = shopMonery;

}

public String getShopMoneryUnit() {

return shopMoneryUnit;

}

public void setShopMoneryUnit(String shopMoneryUnit) {

this.shopMoneryUnit = shopMoneryUnit;

}

public List getShopTags() {

return shopTags;

}

public void setShopTags(List shopTags) {

this.shopTags = shopTags;

}

public static class ShopTagsBean {

/**

* tag : 随便四字

*/

private String tag;

public String getTag() {

return tag;

}

public void setTag(String tag) {

this.tag = tag;

}

}

}

}

主要的功能代码就是这些,一会我会给源码链接,MVP现在怎么也是Android主流的框架,学习掌握一下还是不错的。

云盘链接如果失效或有问题联系dingchao7323@qq.com

欢迎指出不足和缺点!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值