Android MVP设计模式实例详解

Posted Summer-夏天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MVP设计模式实例详解相关的知识,希望对你有一定的参考价值。

 

1.MVP设计模式简介

相信很多开发者对MVP设计模式都有比较深刻的了解,这种设计模式运用非常广。在实际的android项目开发中,MVP设计模式深受开发者的喜爱,因为MVP将前后台完全分离,降低了Model和View的耦合。

MVP,全程:Model-View-Presenter,即模型-视图-层现器。其中,将Activity视为View层,Presenter负责完成View与Model的交互,Model依然是业务逻辑和实体模型。下面用一张图了解数据与视图之间的交互。

 

 

 

MVP模式通过Presenter实现数据和视图之间的交互,简化了Activity的职责。同时即避免了ViewModel的直接联系,又通过Presenter实现两者之间的沟通。

 

2.MVP模式架构图
MVP(Model-View-Presenter)是从MVC的演变过来的,MVP的角色定义:

  • 模型层(Model):主要是获取数据功能,业务逻辑和实体模型。
  • 视图层(View):对应于Activity或Fragment,负责视图的部分展示和业务逻辑用户交互
  • 控制层(Presenter):负责完成View层与Model层间的交互,通过P层来获取M层中数据后返回给V层,使得V层与M层间没有耦合。

在MVP中 ,Presenter层完全将View层和Model层进行了分离,把主要程序逻辑放在Presenter层实现,Presenter与具体的View层(Activity)是没有直接的关联,是通过定义接口来进行交互的,从而使得当View层(Activity)发生改变时,Persenter依然可以保持不变。View层接口类只应该只有set/get方法,及一些界面显示内容和用户输入,除此之外不应该有多余的内容。绝不允许View层直接访问Model层,这是与MVC最大区别之处,也是MVP核心优点。

 

 

3.MVPMVC模式区别
 

 

MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的。

还有一点注意:MVC中V对应的是布局文件,MVP中V对应的是Activity。

1.Activity职责不同,Activity在MVP中是View层,在MVC中是Controller层,这是MVC和MVP很主要的一个区别,可以说Android从MVC转向MVP开发也主要是优化Activity的代码,避免Activity的代码臃肿庞大。

 

2.View层不同,MVC的View层指的是XML布局文件或者是用Java自定义的View,MVP的View层是Activity或者Fragment。使用传统的MVC,其中的View,对应的是各种Layout布局文件,但是这些布局文件中并不像Web端那样强大,能做的事情非常有限。MVP的View层Activity在实际项目中,随着逻辑的复杂度越来越大,Activity臃肿的缺点仍然体现出来了,因为Activity中还是充满了大量与View层无关的代码,比如各种事件的处理派发,就如MVC中的那样View层和Controller代码耦合在一起无法自拔。

 

3.控制层不同,MVC的控制层是Activity,或者是Fragment,Controller对应的是Activity,而Activity中却又具有操作UI的功能,我们在实际的项目中也会有很多UI操作在这一层,也做了很多View中应该做的事情,当然Controller层Activity中也包含Controller应该做的事情,比如各种事件的派发回调,而且在一层中我们会根据事件再去调用Model层操作数据,所以这种MVC的方式在实际项目中,Activity所在的Controller是非常重的,各层次之间的耦合情况也比较严重,不方便单元测试。MVP的控制层是Presenter,里面没有很多的实际东西,主要是做Model和View层的交互。

 

4.关系链不同,MVP中Model层与View是没有关系的,彼此不会通讯和操作,Model与View的通讯都是Presenter层来传达的。但是在MVC中,Model层和View是曾在交互的。比如我们自定义的View控件里面肯定是要使用Model的数据的,View也要根据不同的Model数据做出不同的展现!这点尤其是体现在自定义的View中,自定义View需要设置数据,用户操作了自定义控件需要改变数据,View要操作Model怎么办?有人说把Controller传到自定义的View啊,现实是不可能没一个自定义View都去持有Controller的引用,其实在MVP中就不会这么尴尬,接口就可以完成。

 

5.适用范围不同,在Android中,MVP和MVC都用自己的适用情况,使用MVP可以更好的解耦三大模块,模块之间比较清晰,也很方便使用MVP来组件化架构整体项目。但是MVC也是有用武之地的,在组件化的Module或者中间件我们可以使用MVC来做,Module或者中间件不会存在很复杂的View层,使用MVC可以更加方便我们实现功能。

 

6.交互方式不同,MVP中通讯交互基本都是通过接口的,MVC中的通讯交互很多时候都是实打实的调用对象的方法,简单粗暴!

 

7.实现方法不同 ,MVC和MVP的Model几乎一样的,都是处理数据,只要不在Activity或者Fragment中请求数据,其他的所有控制都放在Activity或者Fragment中,这样写就基本是MVC的模式,这样写不麻烦,但是很容易把Activity写出上万行代码。用MVP的时候我们需要写很多View和Presenter接口来实现模块之间的通讯,会增加很多类。

 

 

4.MVPMVC优缺点对比

(1)相同点:
优点:
1.降低耦合度
2.模块职责划分明显
3.利于测试驱动开发
4.代码复用
5.隐藏数据
6.代码灵活性
缺点:
额外的代码复杂度及学习成本。

(2)不同点:
MVP模式:
1.View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
2.Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
3.通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑,业务相似的时候也可以多同个View共享一个Presenter。
MVC模式:
1.View可以与Model直接交互
2.Controller是基于行为的,并且可以被多个View共享
3.Controller可以负责决定显示哪个View

 

 

5.代码实现

  1. 代码文件结构
  2. 新建model
     
    package com.mvpdemo.model;
    
    /**
     * Title: MVPDemo
     * Package name: com.mvpdemo.model
     * Created by liqh (predestines@163.com)
     * Date: 2018/9/28 17:50
     * Description: description
     */
    public class Phone 
    
        private String telString;
        private String province;
        private String catName;
        private String carrier;
    
        public String getTelString() 
            return telString;
        
    
        public void setTelString(String telString) 
            this.telString = telString;
        
    
        public String getProvince() 
            return province;
        
    
        public void setProvince(String province) 
            this.province = province;
        
    
        public String getCatName() 
            return catName;
        
    
        public void setCatName(String catName) 
            this.catName = catName;
        
    
        public String getCarrier() 
            return carrier;
        
    
        public void setCarrier(String carrier) 
            this.carrier = carrier;
        
    
    


    获取的手机信息包括手机号码、省份、运营商和所属运营商

     
  3. 实现MvpMainView接口,用于抽象出View显示方法
     
    package com.mvpdemo.presenter;
    
    /**
     * Title: MVPDemo
     * Package name: com.mvpdemo.presenter
     * Created by liqh (predestines@163.com)
     * Date: 2018/9/28 17:52
     * Description: description
     */
    public interface MvpMainView extends MvpLoadingView
        void showToash(String msg);
        void updateView();
    
    
    

    两个方法一个为显示信息,另一个用于更新UI。对于继承的MVPLoadingView中两个方法用于显示和隐藏Dialog。
     
  4. 实现MainPresenter类。主要是获取网络数据以及数据的解析等操作
    判断手机号的合法性
     
    public void searchPhoneInfo(String phone)
            if (phone.length() != 11)
                mvpMainView.showToash("请输入正确的手机号");
                return;
            
            mvpMainView.showLoading();
    
        

    处理网络数据
     
    private void sendHttp(String phone) 
            Map<String,String> map = new HashMap<String, String>();
            map.put("tel", phone);
            HttpUntil httpUntil = new HttpUntil(new HttpUntil.HttpResonse() 
                @Override
                public void onSuccess(Object object) 
                    String json = object.toString();
                    int index = json.indexOf("");
                    json = json.substring(index, json.length());
    
                    //JsonObject
    //                mPhone = parseModelWithOrgJson(json);
                    //Gson
                    mPhone = parseModelWithGson(json);
                    //FastJson
    //                mPhone = parseModelWithFastJson(json);
                    mvpMainView.hideLoading();
                    mvpMainView.updateView();
                
    
                @Override
                public void onFail(String error) 
                    mvpMainView.showToash(error);
                    mvpMainView.hideLoading();
                
            );
            httpUntil.sendGetHttp(mUrl, map);
        

    JsonObject方法解析数据:
     
    public Phone parseModelWithOrgJson(String json) 
            Phone phone = new Phone();
            try 
                org.json.JSONObject jsonObject = new org.json.JSONObject(json);
                String value = jsonObject.getString("telString");
                phone.setTelString(value);
    
                value = jsonObject.getString("province");
                phone.setProvince(value);
    
                value = jsonObject.getString("catName");
                phone.setCatName(value);
    
                value = jsonObject.getString("carrier");
                phone.setCarrier(value);
             catch (JSONException e) 
                e.printStackTrace();
            
            return phone;
        


    Gson方法解析数据:
     
    public Phone parseModelWithGson(String json) 
    
            Gson gson = new Gson();
            Phone phone = gson.fromJson(json, Phone.class);
    
            return phone;
        

    FastJson方法解析数据:
     
    public Phone parseModelWithFastJson(String json) 
            Phone phone = JSONObject.parseObject(json, Phone.class);
    
            return phone;
        

    此处为MainPresenter.class完整代码
     
    package com.mvpdemo.presenter.impl;
    
    import com.alibaba.fastjson.JSONObject;
    import com.google.gson.Gson;
    
    import org.json.JSONException;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import com.mvpdemo.business.HttpUntil;
    import com.mvpdemo.model.Phone;
    import com.mvpdemo.presenter.MvpMainView;
    
    /**
     * Title: MVPDemo
     * Package name: com.mvpdemo.presenter.impl
     * Created by liqh (predestines@163.com)
     * Date: 2018/9/28 17:57
     * Description: description
     */
    public class MainPresenter extends BasePresenter
        private MvpMainView mvpMainView;
        private String mUrl = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";
    
        private Phone mPhone;
    
        public Phone getPhoneInfo()
            return mPhone;
        
    
    
        public  MainPresenter(MvpMainView mainView)
            mvpMainView = mainView;
        
    
        public void searchPhoneInfo(String phone)
            if (phone.length() != 11)
                mvpMainView.showToash("请输入正确的手机号");
                return;
            
            mvpMainView.showLoading();
    
            //http请求的处理逻辑
            sendHttp(phone);
    
        
    
        private void sendHttp(String phone) 
            Map<String,String> map = new HashMap<String, String>();
            map.put("tel", phone);
            HttpUntil httpUntil = new HttpUntil(new HttpUntil.HttpResonse() 
                @Override
                public void onSuccess(Object object) 
                    String json = object.toString();
                    int index = json.indexOf("");
                    json = json.substring(index, json.length());
    
                    //JsonObject
    //                mPhone = parseModelWithOrgJson(json);
                    //Gson
                    mPhone = parseModelWithGson(json);
                    //FastJson
    //                mPhone = parseModelWithFastJson(json);
                    mvpMainView.hideLoading();
                    mvpMainView.updateView();
                
    
                @Override
                public void onFail(String error) 
                    mvpMainView.showToash(error);
                    mvpMainView.hideLoading();
                
            );
            httpUntil.sendGetHttp(mUrl, map);
        
    
        public Phone parseModelWithGson(String json) 
    
            Gson gson = new Gson();
            Phone phone = gson.fromJson(json, Phone.class);
    
            return phone;
        
    
        public Phone parseModelWithFastJson(String json) 
            Phone phone = JSONObject.parseObject(json, Phone.class);
    
            return phone;
        
    
        public Phone parseModelWithOrgJson(String json) 
            Phone phone = new Phone();
            try 
                org.json.JSONObject jsonObject = new org.json.JSONObject(json);
                String value = jsonObject.getString("telString");
                phone.setTelString(value);
    
                value = jsonObject.getString("province");
                phone.setProvince(value);
    
                value = jsonObject.getString("catName");
                phone.setCatName(value);
    
                value = jsonObject.getString("carrier");
                phone.setCarrier(value);
             catch (JSONException e) 
                e.printStackTrace();
            
            return phone;
        
    
    
    
    
    
    
    
    
    
    
    
    
    
    

     
  5. 网络请求处理
    get方法
     
    public void sendGetHttp(String url, Map<String, String>param)
            sendHttp(url, param, false);
        
    post方法
     
    public void sendPostHttp(String url, Map<String, String>param)
            sendHttp(url, param, true);
        
    创建网络请求
     
    private Request createRequest(boolean isPost)
            Request request;
            if (isPost)
                MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder();
                requestBodyBuilder.setType(MultipartBody.FORM);
                //遍历map请求参数
                Iterator<Map.Entry<String, String>> iterator = mParam.entrySet().iterator();
                while (iterator.hasNext())
                    Map.Entry<String, String> entry = iterator.next();
                    requestBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
                
                request = new okhttp3.Request.Builder().url(mUrl)
                        .post(requestBodyBuilder.build()).build();
            else
                String urlStr = mUrl;
                //带参请求
                if (mParam != null)
                    urlStr = mUrl + "?" + MapParamToString(mParam);
                
                request = new Request.Builder().url(urlStr).build();
            
            return request;
        

    完整代码
     
    package com.mvpdemo.business;
    
    import android.os.Handler;
    import android.os.Looper;
    
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.MultipartBody;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    
    /**
     * Title: MVPDemo
     * Package name: com.mvpdemo.business
     * Created by liqh (predestines@163.com)
     * Date: 2018/9/28 18:05
     * Description: description
     */
    public class HttpUntil 
        String mUrl;
        Map<String, String> mParam;
        HttpResonse mHttpResonse;
    
        Handler myHandler = new Handler(Looper.getMainLooper());
    
        private final OkHttpClient client = new OkHttpClient();
    
    
    
        public HttpUntil(HttpResonse httpResonse)
            mHttpResonse = httpResonse;
        
    
        public interface  HttpResonse
            void onSuccess(Object object);
            void onFail(String error);
        
    
        public void sendPostHttp(String url, Map<String, String>param)
            sendHttp(url, param, true);
        
    
        public void sendGetHttp(String url, Map<String, String>param)
            sendHttp(url, param, false);
        
    
        public void sendHttp(String url, Map<String, String>param, boolean isPost)
            mUrl = url;
            mParam = param;
            run(isPost);
        
    
    
        private  void run(boolean isPost)
            Request request = createRequest(isPost);
            client.newCall(request).enqueue(new Callback() 
                @Override
                public void onFailure(Call call, IOException e) 
                    if(mHttpResonse != null)
                        myHandler.post(new Runnable() 
                            @Override
                            public void run() 
                                mHttpResonse.onFail("Fail");
                            
                        );
                    
                
    
                @Override
                public void onResponse(Call call, final Response response) throws IOException 
                    if(mHttpResonse == null) return;
                    myHandler.post(new Runnable() 
                        @Override
                        public void run() 
                            if (!response.isSuccessful())
                                mHttpResonse.onFail("fail,code:" + response);
                            else
                                try 
                                    mHttpResonse.onSuccess(response.body().string());
                                 catch (IOException e) 
                                    e.printStackTrace();
                                    mHttpResonse.onFail("转换失败");
                                
                            
                        
                    );
                
            );
        
        private Request createRequest(boolean isPost)
            Request request;
            if (isPost)
                MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder();
                requestBodyBuilder.setType(MultipartBody.FORM);
                //遍历map请求参数
                Iterator<Map.Entry<String, String>> iterator = mParam.entrySet().iterator();
                while (iterator.hasNext())
                    Map.Entry<String, String> entry = iterator.next();
                    requestBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
                
                request = new okhttp3.Request.Builder().url(mUrl)
                        .post(requestBodyBuilder.build()).build();
            else
                String urlStr = mUrl;
                //带参请求
                if (mParam != null)
                    urlStr = mUrl + "?" + MapParamToString(mParam);
                
                request = new Request.Builder().url(urlStr).build();
            
            return request;
        
    
    
        private String MapParamToString(Map<String, String>param)
            StringBuilder stringBuilder = new StringBuilder();
            Iterator<Map.Entry<String, String>> iterator = param.entrySet().iterator();
            while (iterator.hasNext())
                Map.Entry<String, String> entry = iterator.next();
                stringBuilder.append(entry.getKey() + "=" + entry.getValue() + "&");
            
            String str = stringBuilder.toString().substring(0, stringBuilder.length());
            return str;
        
    
    
    
    
    
    
    
    
    
    
    
    
    
    

     
  6. MainActivity实现相关UI的显示及接口的处理
    获取数据后更新界面
     
    @Override
        public void updateView() 
           Phone phone = mainPresenter.getPhoneInfo();
           if (phone != null)
               mShowInfoLayout.setVisibility(View.VISIBLE);
               result_phone.setText("手机号:" + phone.getTelString());
               result_province.setText("省份:" + phone.getProvince());
               result_type.setText("运营商:" + phone.getCatName());
               result_carrier.setText("归属运营商:" + phone.getCarrier());
           else
               mShowInfoLayout.setVisibility(View.GONE);
           
    
        
    实现MVPLoadingView中方法,显示加载弹窗与隐藏弹窗
     
    @Override
        public void showLoading() 
            if (progressDialog == null)
                progressDialog = ProgressDialog.show(this, "", "正在加载", true,false);
            else if (progressDialog.isShowing())
                progressDialog.setTitle("");
                progressDialog.setMessage("正在加载...");
            
            progressDialog.show();
        
    @Override
        public void hideLoading() 
            if (progressDialog != null && progressDialog.isShowing())
                progressDialog.dismiss();
            
        


     
  7. 实现效果图
     

 

6.总结
MVP框架模式完全将Model模型和View视图分离,从而使得代码的耦合低,利用MVP框架写项目达到解耦作用。 MVP和MVC最大的区别是:MVC中的V和C关系比较紧密,耦合度太高,从C中访问M获取数据一定程度上也可以看成从V中访问M。而MVP中M和V完全分离,互相不知道对方的存在,Presenter通过接口通信方式将V和M通信。 在Android中MVP框架 Activity担当View视图层,MVC框架模式Activity担当控制器。
 

7.源码下载
下载地址:https://download.csdn.net/download/u012721519/10706788

 

 

 

Good luck!

Write by Jimmy.li


 

 

 

 

以上是关于Android MVP设计模式实例详解的主要内容,如果未能解决你的问题,请参考以下文章

Android进阶之路-详解MVP

作为过来人,对于Android MVP模式的一些详解

结合实例分析Android MVP的实现

Android MVP架构模式

热门前沿知识相关面试问题-MVC/MVP/MVVM架构设计模式面试问题详解

Android:安卓学习笔记之MVP模式的简单理解和使用