王学岗csdn——MVP架构模式详解(一)

来源:互联网 发布:python的素数怎么表示 编辑:程序博客网 时间:2024/06/02 02:41

前面我写过MVP模式,但是过于浅显,今天我在来详细的探究下该模式

1、什么是MVP?        通俗讲解:就是通过Presenter将View和Mode解耦        M(模型)->Model(模型)包括:与数据相关都属于M层(例如:数据库、文件、网络、数据解析、数据保存等等......)bean也属于M层,但M层不单单指M层        V->View包括:在MVC中View只是一个单纯视图,但是在MVP中(例如:Activity、Fragment、布局),即:与UI相关都属于V层           P->Presenter包括:调度,通过P层将我们的View层和Model进行关联转换,将M转换为V,同时使M和V解耦    2、MVP和Java设计模式有什么区别?        举例说明:北京鸟巢(整体架构)---MVP---范围大而广                   窗户设计、场地设计、跑到设计、座位设计等等......---相当于设计模式---针对具体的问题或者说场景提出不同解决方案大家思考下,如果你连鸟巢整体架构都没有,那你单单弄一个窗户设计有意义吗?    3、MVP架构交互流程?    4、案例分析?---登录为例

我们看下使用MVP如何实现一个简单的网络登录
首先看下各个类名这里写图片描述
三个Utils类,这三个类实现了网络的请求,当然你完全可以换成OKHttp请求或者Retrofit

package com.example.acer.myfirstmvp.utils;import android.os.AsyncTask;public class HttpTask extends AsyncTask<String, Void, String> {    private HttpUtils.OnHttpResultListener onHttpResultListener;    public HttpTask(HttpUtils.OnHttpResultListener onHttpResultListener) {        this.onHttpResultListener = onHttpResultListener;    }    @Override    protected String doInBackground(String... params) {        try {            return HttpUtils.post(params[0], params[1], params[2]);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    @Override    protected void onPostExecute(String result) {        if (this.onHttpResultListener != null) {            this.onHttpResultListener.onResult(result);        }    }}
package com.example.acer.myfirstmvp.utils;import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;public class HttpUtils {    public static final String URL_STR = "http://192.168.57.1:8080/Dream_4_23_PhoneGapServer/PhoneGapServlet";    public static String get(String urlStr) {        String result = null;        try {            URL url = new URL(urlStr);            HttpURLConnection connection = (HttpURLConnection) url                    .openConnection();            connection.setReadTimeout(5000);            connection.setRequestMethod("GET");            connection.setDoInput(true);            if (connection.getResponseCode() == 200) {                InputStream inStream = connection.getInputStream();                result = new String(StreamTool.readInputStream(inStream));                return result;            }        } catch (Exception e) {            e.printStackTrace();        }        return result;    }    public static String post(String urlStr, String username, String password)            throws Exception {        StringBuffer sb = null;        String param = "username=" + username + "&password=" + password;        URL url = new URL(urlStr);        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();        // 设置参数        httpConn.setDoOutput(true); // 需要输出        httpConn.setDoInput(true); // 需要输入        httpConn.setUseCaches(false); // 不允许缓存        httpConn.setRequestMethod("POST"); // 设置POST方式连接        // 设置请求属性        httpConn.setRequestProperty("Charset", "UTF-8");        // 连接,也可以不用明文connect,使用下面的httpConn.getOutputStream()会自动connect        httpConn.connect();        // 建立输入流,向指向的URL传入参数        DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());        dos.writeBytes(param.toString());        dos.flush();        dos.close();        // 获得响应状态        int resultCode = httpConn.getResponseCode();        sb = new StringBuffer();        if (HttpURLConnection.HTTP_OK == resultCode) {            String readLine = new String();            //解析网络请求数据            BufferedReader responseReader = new BufferedReader(                    new InputStreamReader(httpConn.getInputStream(), "UTF-8"));            while ((readLine = responseReader.readLine()) != null) {                sb.append(readLine).append("\n");            }            responseReader.close();            return sb.toString();        }        return null;    }    public interface OnHttpResultListener {        public void onResult(String result);    }}
package com.example.acer.myfirstmvp.utils;import java.io.ByteArrayOutputStream;import java.io.InputStream;public class StreamTool {    /**     * 从输入流中读取数据     * @param inStream     * @return     * @throws Exception     */    public static byte[] readInputStream(InputStream inStream) throws Exception{        ByteArrayOutputStream outStream = new ByteArrayOutputStream();        byte[] buffer = new byte[1024];        int len = 0;        while( (len = inStream.read(buffer)) !=-1 ){            outStream.write(buffer, 0, len);        }        byte[] data = outStream.toByteArray();//网页的二进制数据        outStream.close();        inStream.close();        return data;    }}

三个没用的类贴完了,下面我们来贴出我们的MVP相关的类,在代码中都做注释了,这里就不废话了,代码很简单;
M类,

package com.example.acer.myfirstmvp.simple2;import com.example.acer.myfirstmvp.utils.HttpTask;import com.example.acer.myfirstmvp.utils.HttpUtils;/** * M层(数据网络都在这一层) */public class MainModel {    public void login(String username, String pwd, final HttpUtils.OnHttpResultListener onHttpResultListener){        HttpTask httpTask=new HttpTask(new HttpUtils.OnHttpResultListener() {            @Override            public void onResult(String result) {                //解析数据                //更新UI                onHttpResultListener.onResult(result);            }        });        httpTask.execute(username,pwd,"http://www.baidu.com");    }}

V类,首先要有一个接口

package com.example.acer.myfirstmvp.simple2;/** * View接口,通过View暴露接口去访问 */public interface MainView {    public void onLoginResult(String result);}
package com.example.acer.myfirstmvp.simple2;import android.os.Bundle;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.widget.Toast;import com.example.acer.myfirstmvp.R;/** * MVPP将View(本例子中的Activity)和相关操作进行了完全的隔离,将来你修改网络请求方式(比如修改成OKhttp),丝毫不影响UI,UI只需要 * 通过onLoginResult拿到该结果;更加方便团队开发,大大提高了开发效率 */public class MainActivity2 extends AppCompatActivity implements MainView{private MainPresenter mMainPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(toolbar);        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);        fab.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)                        .setAction("Action", null).show();            }        });        mMainPresenter=new MainPresenter(this);    }     public void click(View v){         this.mMainPresenter.login("Dream","123");     }    @Override    public void onLoginResult(String result) {      //更新UI        Toast.makeText(this,result,Toast.LENGTH_LONG).show();    }}

p类,关键的类

package com.example.acer.myfirstmvp.simple2;import com.example.acer.myfirstmvp.utils.HttpUtils;/** * P层,对M和V进行关联 */public class MainPresenter {    private MainView mainView;    private MainModel maiModel;    public MainPresenter(MainView mainView){        this.maiModel=new MainModel();        this.mainView=mainView;    }    public void login(String username,String pwd){        this.maiModel.login(username, pwd, new HttpUtils.OnHttpResultListener() {            @Override            public void onResult(String result) {                mainView.onLoginResult(result);            }        });    }}

当然这个例子太简单,实际开发中没毛线用;举个两个例子

问题一:            假设:Activity意外关闭,这个时候网络请求还在进行,当数据返回的时候,发现Activity挂掉了?(或者说Fragment)---挂掉了你还要回调,这可是一个垃圾操作啊            该怎么办?---解决办法:提供一个销毁的方法

大家看下我们的解决方案
首先修改下P

package com.example.acer.myfirstmvp.simple3;import com.example.acer.myfirstmvp.utils.HttpUtils;/** * P层,对M和V进行关联 */public class MainPresenter {    private MainView mainView;    private MainModel maiModel;    //    public MainPresenter(MainView mainView){    //        this.maiModel=new MainModel();    //        this.mainView=mainView;    //    }    //改动的地方    **public MainPresenter() {        //改动的地方        this.maiModel = new MainModel();    }**    public void login(String username, String pwd) {        this.maiModel.login(username, pwd, new HttpUtils.OnHttpResultListener() {            @Override            public void onResult(String result) {                if (mainView != null) {                    mainView.onLoginResult(result);                }            }        });    }    **//改动的地方    //绑定视图层    public void attachView(MainView mainView) {        this.mainView = mainView;    }    //改动的地方    //解除绑定    public void detachView() {        this.mainView = null;    }**}

P增加了两个方法(绑定和解除绑定的方法),同时修改下M层

package com.example.acer.myfirstmvp.simple3;import android.os.Bundle;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.widget.Toast;import com.example.acer.myfirstmvp.R;/** * MVPP将View(本例子中的Activity)和相关操作进行了完全的隔离,将来你修改网络请求方式(比如修改成OKhttp),丝毫不影响UI,UI只需要 * 通过onLoginResult拿到该结果;更加方便团队开发,大大提高了开发效率 */public class MainActivity2 extends AppCompatActivity implements MainView {private MainPresenter mMainPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(toolbar);        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);        fab.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)                        .setAction("Action", null).show();            }        });        **//修改的地方//        mMainPresenter=new MainPresenter(this);        mMainPresenter=new MainPresenter();        this.mMainPresenter.attachView(this);**    }     public void click(View v){         this.mMainPresenter.login("Dream","123");     }    @Override    public void onLoginResult(String result) {      //更新UI        Toast.makeText(this,result,Toast.LENGTH_LONG).show();    }    **//修改的地方    @Override    protected void onDestroy() {        super.onDestroy();        this.mMainPresenter.detachView();    }**}

但我们这样依然不完美,举个例子

项目开发当中Activity数量,或者说Fragment数量很庞大,这样会有很多代码冗余            该怎么办?---解决方案:单独抽象出来(引出抽象类)

下面我们看下,抽象类如何设计MVP
先看下类的结构这里写图片描述

package com.tz.architect.mvp.simple4;import android.os.Bundle;import android.view.View;import android.widget.Toast;import com.tz.architect.mvp.R;import com.tz.architect.mvp.simple4.framework.impl.MainPresenter;import com.tz.architect.mvp.simple4.framework.impl.MainView;import com.tz.architect.mvp.simple4.framework.impl.MvpActivity;public class MainActivity extends MvpActivity<MainView, MainPresenter> implements MainView {    @Override    public MainPresenter bindPresenter() {        return new MainPresenter();    }    @Override    public MainView bindView() {        return this;    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    // 其实在Android当中,本身就是一个非常典型MVC架构    // 在Android MVC中    // M代表:数据    // C代表: activity或者Fragment    // V代表:视图    // MVP适合大项目    // MVP更加便于团队开发    public void click(View v) {        getPresenter().login("Dream", "123456");    }    @Override    public void onLoginResult(String result) {        // 更新UI        Toast.makeText(this, result, Toast.LENGTH_LONG).show();    }}
package com.tz.architect.mvp.simple4.framework;public abstract class AbsMvpPresenter<V extends IMvpView> {    private V view;    public V getView() {        return view;    }    /**     * 绑定     *      * @param view     */    public void attachView(V view) {        this.view = view;    }    /**     * 解除绑定     */    public void detachView() {        this.view = null;    }}
package com.tz.architect.mvp.simple4.framework;public interface IMvpView {}
package com.tz.architect.mvp.simple4.framework.impl;import com.tz.architect.mvp.utils.HttpTask;import com.tz.architect.mvp.utils.HttpUtils.OnHttpResultListener;/** * M层(数据、网络) * @author Dream * */public class MainModel {    public void login(String username,String pwd,final OnHttpResultListener onHttpResultListener){        HttpTask httpTask = new HttpTask(new OnHttpResultListener() {            @Override            public void onResult(String result) {                onHttpResultListener.onResult(result);            }        });        httpTask.execute(username,pwd,"http://www.baidu.com");    }}
package com.tz.architect.mvp.simple4.framework.impl;import com.tz.architect.mvp.simple4.framework.AbsMvpPresenter;import com.tz.architect.mvp.utils.HttpUtils.OnHttpResultListener;/** * P层 *  * @author Dream * */public class MainPresenter extends AbsMvpPresenter<MainView> {    private MainModel mainModel;    public MainPresenter() {        this.mainModel = new MainModel();    }    public void login(String username, String pwd) {        this.mainModel.login(username, pwd, new OnHttpResultListener() {            @Override            public void onResult(String result) {                if (getView() != null) {                    getView().onLoginResult(result);                }            }        });    }}
package com.tz.architect.mvp.simple4.framework.impl;import com.tz.architect.mvp.simple4.framework.IMvpView;/** * View接口 * @author Dream *  */public interface MainView extends IMvpView{    public void onLoginResult(String result);}
package com.tz.architect.mvp.simple4.framework.impl;import com.tz.architect.mvp.simple4.framework.AbsMvpPresenter;import com.tz.architect.mvp.simple4.framework.IMvpView;import android.app.Activity;import android.os.Bundle;public abstract class MvpActivity<V extends IMvpView, P extends AbsMvpPresenter<V>>        extends Activity {    private P presenter;    private V view;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (presenter == null) {            this.presenter = bindPresenter();        }        if (view == null) {            this.view = bindView();            this.presenter.attachView(this.view);        }    }    /**     * 绑定P层     *      * @return     */    public abstract P bindPresenter();    /**     * 绑定View层     *      * @return     */    public abstract V bindView();    public P getPresenter() {        return presenter;    }    public V getView() {        return view;    }    @Override    protected void onDestroy() {        super.onDestroy();        if (this.presenter != null) {            this.presenter.detachView();        }    }}

即便如此,这个框架仍然不完整,因为我们这里只有Activity而缺少Fragment,我们继续完善;