继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

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

丁先森Promise
关注TA
已关注
手记 3
粉丝 9
获赞 70

转载请注明出处:https://www.cnblogs.com/dingxiansen/

  丁先森 博客园
MVP 理论知识

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

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

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

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

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

那Model 层如何反馈给Presenter 层的呢?Presenter 又是如何操控View 层呢?看图!
图片描述
图是我借来的

上图中说明了低层的不会直接给上一层做反馈,而是通过 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。

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

先看一下目录结构
图片描述

这里肯定会有人说,我去每次创建新功能,不能每次创建那么多类吧,那不得麻烦死,这里推荐一个AndroidStudio的插件,AndroidMVP,看一下这个插件能实现的功能
图片描述
图还是借来的,这个还是挺好用的,看完插件那就再看看代码实现的效果吧,毕竟能看到才知道实现了什么效果
看完效果图,来看看代码是怎么实现的
图片描述
倒着来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> T request(int requestFlag);

    <T> 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<JsonDataBean.HomeShoplistBean> 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> T request(int requestFlag) {
        return null;
    }

    @Override
    public <T> 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<String>() {
                @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<String, String> getParams() throws AuthFailureError {
                    Map<String, String> map = new HashMap<String, String>();
                    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<HomeImgurlBean> home_imgurl, List<HomeH5urlBean> home_h5url, List<HomeNewsBean> home_news, List<HomeShoplistBean> 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<HomeImgurlBean> home_imgurl;
    private List<HomeH5urlBean> home_h5url;
    private List<HomeNewsBean> home_news;
    private List<HomeShoplistBean> 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<HomeImgurlBean> getHome_imgurl() {
        return home_imgurl;
    }

    public void setHome_imgurl(List<HomeImgurlBean> home_imgurl) {
        this.home_imgurl = home_imgurl;
    }

    public List<HomeH5urlBean> getHome_h5url() {
        return home_h5url;
    }

    public void setHome_h5url(List<HomeH5urlBean> home_h5url) {
        this.home_h5url = home_h5url;
    }

    public List<HomeNewsBean> getHome_news() {
        return home_news;
    }

    public void setHome_news(List<HomeNewsBean> home_news) {
        this.home_news = home_news;
    }

    public List<HomeShoplistBean> getHome_shoplist() {
        return home_shoplist;
    }

    public void setHome_shoplist(List<HomeShoplistBean> 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<ShopTagsBean> 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<ShopTagsBean> getShopTags() {
            return shopTags;
        }

        public void setShopTags(List<ShopTagsBean> 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主流的框架,学习掌握一下还是不错的。

代码下载:链接:https://pan.baidu.com/s/1DVZ73LHg7KwGVU0Zp6HAqA 密码:pqdc

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

欢迎指出不足和缺点!

打开App,阅读手记
7人推荐
发表评论
随时随地看视频慕课网APP