MVP架构

此处输入图片的描述

MVP框架担任角色

Model: 处理数据,包过网络请求、提交数据;缓存、读写本地数据、写入;写入、读取数据库数据等

View: 提供Activity需要实现方法,本身是一个接口,实际在Presenter中被调用执行。

Presenter: 业务处理,内部持有Model和View;使用CallBack获取Model获取结果信息。

Activity: 实现View提供方法,持有Presenter对象;在监听事件中调用Presenter方法,其他初始化与之前MVC一样。

使用优缺点

优点:
1、解耦:对于页面处理差异性、兼容性非常好;在于请求数据参数可能需要改变情况可以很好做到隔离性,在Model可以处理,不需要改变Activity流程。

2、分解:简化代码、将一个类中几百上千行都拆分处理,使得整个流程看起来更清晰。

缺点:
1、建立类多、容易造成内存泄漏,对于不熟悉Mvp上手有点难度、不好调试出现bug,不清楚执行方法流向。

总结:在对于单一功能、逻辑不复杂的页面并不建议使用MVP模式分解;所以最后看项目适用情况,哪个页面需要MVP分解,大多页面使用MVC即可。

实例

Demo结构

此处输入图片的描述

解耦

需求:对于不同地区app登录检车业务、其中各个地区以内网开发;登录参数可能由于不同地区有细微差异性。登录页面需要高度统一不变的前提下,使用MVP就是量身打造。

IView 接口

public interface IView {
    void show();
    void hide();
    void toMainActivity();
    void tip(String msg);
}

这个接口主要负责对于Activity一些形态,比如提示信息,显示进度条,调转其他页面等

LoginActivity 页面

public class LoginActivity extends AppCompatActivity implements IView,View.OnClickListener{

    private IPresenter presenter;
    private ProgressDialog progressDialog;
    private EditText et_name;
    private EditText et_password;
    private Button btn;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_name=findViewById(R.id.username);
        et_password=findViewById(R.id.password);

        btn=findViewById(R.id.btn);
        btn.setOnClickListener(this);

        presenter=new IPresenter(this,new IMode());
        progressDialog=new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在请求...");


    }

    @Override
    public void show() {
        progressDialog.show();
    }

    @Override
    public void hide() {
        progressDialog.hide();
    }

    @Override
    public void toMainActivity() {
        startActivity(new Intent(this, HomeActivity.class));
    }

    @Override
    public void tip(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();
    }

    @Override
    public void onClick(View view) {

        switch (view.getId()){

            case R.id.btn:
                String name=et_name.getText().toString().trim();
                String password=et_password.getText().toString().trim();

                presenter.login(name,password);

                break;

                default:
                    break;
        }
    }
}

这个页面LoginActivit对于初始化控件及点击事件、获取控件数据操作依旧保留不变,对于实现view方法具体由Presenter调用。

IMode 处理数据

public class IMode {

    /**
     * 登录接口
     * @param url
     * @param map
     * @param callback
     */
    public void login(String url, Map map , StringCallback callback){
        OkHttpManager.get(url,map).execute(callback);
    }


    /**
     * 获取当前站点接口
     */
    public void getNowWhere(String url,StringCallback callback){

        OkHttpManager.get(url).execute(callback);

    }


}

这个Imode处理数据,请求网络,结果StringCallBack回调,并且将结果返回到Presenter处理。上面使用的是鸿洋大神OKhttpUtils框架。

IPresenter 业务处理

public class IPresenter  {

    private IView view;
    private IMode mode;

    public IPresenter(IView view,IMode mode){
        this.view=view;
        this.mode=mode;
    }
    public void login(final String name, final String psw){

        if(TextUtils.isEmpty(name)){
            if(isViewAttached()){
                view.tip("用戶名為空!");
            }
            return;
        }

        if(TextUtils.isEmpty(psw)){
            if(isViewAttached()){
                view.tip("密碼為空!");
            }
            return;
        }

        if(isViewAttached()){
            view.show();
        }

        //延迟一秒,请求数据;显示进度条作用
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

                mode.getNowWhere(NetUrl.KEY_WHERE, new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {

                        if(isViewAttached()){
                            view.hide();
                            view.tip("获取站点信息错误:"+e.getMessage());
                        }
                    }

                    @Override
                    public void onResponse(String response, int id) {

                        if(isViewAttached()){
                            view.hide();
                        }
                        String[] where={"A","B","C","D"};
                        int index=new Random().nextInt(4);
                        String now=where[index];

                        Log.e("QISHUI","当前登录站:"+now);

                        switch (now){

                            case "A":
                                loginA(name,psw);
                                break;
                            case "B":
                                loginB(name,psw);
                                break;
                            case "C":
                                loginC(name,psw);
                                break;
                            case "D":
                                loginD(name,psw);
                                break;
                            default:
                                break;

                        }


                    }
                });

            }
        },1000);





    }


    /**
     * A站点登录
     */
    public void loginA(String name ,String password){


        if(isViewAttached()){
            view.show();
        }

        Map<String,String> map=new HashMap();
        map.put("namea",name);
        map.put("pwda",password);


        mode.login(NetUrl.KEY_WHERENOW, map, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {

                if(isViewAttached()){
                    view.hide();
                    view.tip("登录A信息错误:"+e.getMessage());
                }
            }

            @Override
            public void onResponse(String response, int id) {
                if(isViewAttached()){
                    view.hide();
                    view.toMainActivity();
                    //其他处理
                }
            }
        });

    }

    /**
     * B站点登录
     */
    public void loginB(String name ,String password){


        if(isViewAttached()){
            view.show();
        }

        Map<String,String> map=new HashMap();
        map.put("nameb",name);
        map.put("pwdb",password);
        map.put("mac","1s:12:34:67:rt:13");

        mode.login(NetUrl.KEY_WHERENOW, map, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {

                if(isViewAttached()){
                    view.hide();
                    view.tip("登录B信息错误:"+e.getMessage());
                }
            }

            @Override
            public void onResponse(String response, int id) {
                if(isViewAttached()){
                    view.hide();
                    view.toMainActivity();
                    //其他处理
                }
            }
        });

    }

    /**
     * C站点登录
     */
    public void loginC(String name ,String password){

        if(isViewAttached()){
            view.show();
        }

        Map<String,String> map=new HashMap();
        map.put("namec",name);
        map.put("pwdc",password);
        map.put("imei","123456789044444");

        mode.login(NetUrl.KEY_WHERENOW, map, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {

                if(isViewAttached()){
                    view.hide();
                    view.tip("登录C信息错误:"+e.getMessage());
                }
            }

            @Override
            public void onResponse(String response, int id) {
                if(isViewAttached()){
                    view.hide();
                    view.toMainActivity();
                    //其他处理
                }
            }
        });

    }

    /**
     * D站点登录
     */
    public void loginD(String name ,String password){


        if(isViewAttached()){
            view.show();
        }

        Map<String,String> map=new HashMap();
        map.put("named",name);
        map.put("pwdd",password);
        map.put("imei","123456789044444");
        map.put("mac","1s:12:34:67:rt:13");

        mode.login(NetUrl.KEY_WHERENOW, map, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {

                if(isViewAttached()){
                    view.hide();
                    view.tip("登录D信息错误:"+e.getMessage());
                }
            }

            @Override
            public void onResponse(String response, int id) {
                if(isViewAttached()){
                    view.hide();
                    view.toMainActivity();
                    //其他处理
                }
            }
        });

    }


    public void attachView(IView v) {this.view= v;}

    public void detachView() {
        this.view= null;
    }

    public boolean isViewAttached(){
        return view!= null;
    }



}

MVP中主要角色,将Actiivty中View方法调用,同时处理IMode数据

其他

NetUrl.class

public class NetUrl {

    public static final String KEY_WHERE = "https://www.baidu.com";
    public static final String KEY_WHERENOW = "https://www.baidu.com";

}

保存API地址

OkHttpManager.class

public class OkHttpManager {

    // get 异步请求
    public static RequestCall get(String url, Map<String, String> params) {
        printLog(url,params);
        return OkHttpUtils.get().url(url).params(params).build();
    }
    // get 异步请求
    public static RequestCall get(String url) {
        printLog(url,null);
        return  OkHttpUtils.get().url(url).build();
    }

    //post请求 异步请求
    public static RequestCall post(String url, Map<String, String> params) {
        printLog(url,params);
        return  OkHttpUtils.post().url(url).params(params).build();
    }

    //post请求 异步请求
    public static RequestCall post(String url) {
        printLog(url,null);
        return OkHttpUtils.post().url(url).build();
    }

    //post一个json类型数据
    public static RequestCall postJson(String url, String jsonData) {
        printLog(url,jsonData);
        return OkHttpUtils.postString().url(url).content(jsonData).mediaType(MediaType.parse("application/json; charset=utf-8")).build();
    }

    // post一个文件
    public static RequestCall postFile(String url, File file) {
        printLog(url,file.getAbsolutePath());
        return OkHttpUtils.postFile().url(url).file(file).build();
    }


    // 下载文件
    public static void downloadFile(String url, String path, String name) {
        printLog(url,path);
        get(url).execute(new FileCallBack(path, name)
        {
            @Override
            public void onAfter(int id) {
                super.onAfter(id);
            }

            @Override
            public void onError(Call call, Exception e, int id) {

            }

            @Override
            public void onResponse(File response, int id) {

            }

            @Override
            public void inProgress(float progress, long total, int id) {
            }
        });
    }



    //取消请求
    public static void cancel(String url) {
        OkHttpUtils.get().url(url).build().cancel();
    }


    /**
     * 打印路径信息
     *
     * @param url
     */
    private static void printLog(String url,Object obj) {
        StringBuilder sb = new StringBuilder();
        sb.append(url).append("?");

        if(obj instanceof Map){
            Map<String, String> params= (Map<String, String>) obj;
            if (params != null) {
                for (String s : params.keySet()) {
                    sb.append(s).append("=").append(params.get(s)).append("&");
                }
            }
        }else {
            if (obj!=null){
                sb.append(obj);
            }
        }
        Log.e("QISHUI",sb.toString());
    }
}

简易处理网络请求

activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="用户名"/>


    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密码"/>

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登录"/>

</LinearLayout> 

至于HomeActivity就是新建一个页面,没有任何其他逻辑。

运行输出

06-29 11:18:35.244 8518-8518/com.vkeline.mvpdemo E/QISHUI: https://www.baidu.com?
06-29 11:18:35.379 8518-8518/com.vkeline.mvpdemo E/QISHUI: 当前登录站:A
06-29 11:18:35.379 8518-8518/com.vkeline.mvpdemo E/QISHUI: https://www.baidu.com?pwda=cgg&namea=qwe&
06-29 11:18:39.001 8518-8518/com.vkeline.mvpdemo E/QISHUI: https://www.baidu.com?
06-29 11:18:39.053 8518-8518/com.vkeline.mvpdemo E/QISHUI: 当前登录站:C
06-29 11:18:39.053 8518-8518/com.vkeline.mvpdemo E/QISHUI: https://www.baidu.com?imei=123456789044444&pwdc=cgg&namec=qwe&
06-29 11:18:42.960 8518-8518/com.vkeline.mvpdemo E/QISHUI: https://www.baidu.com?

分解

需求:录入几十条数据,并对数据验证,缓存,清理已填数据等,这种类型适合使用MVP细化分解。

ISubmitModel.class 提交数据

public class ISubmitModel {

    /**
     * 提交数据
     * @param url
     * @param map
     * @param callback
     */
    public void submit(String url , Map map, StringCallback callback){
        OkHttpManager.get(url,map).execute(callback);
    }
}

ISubmitView.class 业务接口、功能点

public interface ISubmitView {


    void initSet();

    void tip(String message);

    void save();
}

ISubmitPresenter 业务处理

public class ISubmitPresenter {

    private ISubmitModel model;
    private ISubmitView view;


    public ISubmitPresenter(ISubmitView view, ISubmitModel model) {
        this.view = view;
        this.model = model;
    }


    public void submit(SubmitBean submitBean) {

        if (!check(submitBean)) {
            return;
        }

        Map map = new HashMap();
        map.put("sbstring", new Gson().toJson(submitBean));

        model.submit(NetUrl.KEY_SUBMIT, map, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {

                if (isViewAttached()) {
                    view.tip("提交失败");
                }
            }

            @Override
            public void onResponse(String response, int id) {

                if (isViewAttached()) {
                    view.tip("提交成功!");
                    view.initSet();
                    SPUtils.remove(SubmitActivity.KEY_All);
                }

            }
        });
    }

    /**
     * @param submitBean
     */
    private Boolean check(SubmitBean submitBean) {

        if (TextUtils.isEmpty(submitBean.getEt1())) {
            if (isViewAttached()) {
                view.tip("et1 null");
            }
            return false;
        }

        if (TextUtils.isEmpty(submitBean.getEt2())) {
            if (isViewAttached()) {
                view.tip("et2 null");
            }
            return false;
        }

        if (TextUtils.isEmpty(submitBean.getEt3())) {
            if (isViewAttached()) {
                view.tip("et3 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt4())) {
            if (isViewAttached()) {
                view.tip("et4 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt5())) {
            if (isViewAttached()) {
                view.tip("et5 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt6())) {
            if (isViewAttached()) {
                view.tip("et6 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt7())) {
            if (isViewAttached()) {
                view.tip("et7 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt8())) {
            if (isViewAttached()) {
                view.tip("et8 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt9())) {
            if (isViewAttached()) {
                view.tip("et9 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt10())) {
            if (isViewAttached()) {
                view.tip("et10 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt11())) {
            if (isViewAttached()) {
                view.tip("et11 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt12())) {
            if (isViewAttached()) {
                view.tip("et12 null");
            }
            return false;
        }
        if (TextUtils.isEmpty(submitBean.getEt13())) {
            if (isViewAttached()) {
                view.tip("et13 null");
            }
            return false;
        }

        return true;
    }


    public void initSet() {
        if (isViewAttached()) {
            view.initSet();
        }
    }

    public void save(SubmitBean submitBean) {
        if (isViewAttached()) {
            view.save();
        }
    }

    public void attachView(ISubmitView v) {
        this.view = v;
    }

    public void detachView() {
        this.view = null;
    }

    public boolean isViewAttached() {
        return view != null;
    }

}

SubmitActivity.calss

public class SubmitActivity extends AppCompatActivity implements ISubmitView, View.OnClickListener {

    // Content View Elements

    private Button mBtn1;
    private Button mBtn2;
    private Button mBtn3;
    private EditText mEt1;
    private EditText mEt2;
    private EditText mEt3;
    private EditText mEt4;
    private EditText mEt5;
    private EditText mEt6;
    private EditText mEt7;
    private EditText mEt8;
    private EditText mEt9;
    private EditText mEt10;
    private EditText mEt11;
    private EditText mEt12;
    private EditText mEt13;
    private ISubmitPresenter presenter;
    public static final String KEY_All = "All";

    private void bindViews() {
        mBtn1 = (Button) findViewById(R.id.btn1);
        mBtn2 = (Button) findViewById(R.id.btn2);
        mBtn3 = findViewById(R.id.btn3);
        mEt1 = (EditText) findViewById(R.id.et1);
        mEt2 = (EditText) findViewById(R.id.et2);
        mEt3 = (EditText) findViewById(R.id.et3);
        mEt4 = (EditText) findViewById(R.id.et4);
        mEt5 = (EditText) findViewById(R.id.et5);
        mEt6 = (EditText) findViewById(R.id.et6);
        mEt7 = (EditText) findViewById(R.id.et7);
        mEt8 = (EditText) findViewById(R.id.et8);
        mEt9 = (EditText) findViewById(R.id.et9);
        mEt10 = (EditText) findViewById(R.id.et10);
        mEt11 = (EditText) findViewById(R.id.et11);
        mEt12 = (EditText) findViewById(R.id.et12);
        mEt13 = (EditText) findViewById(R.id.et13);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_submit);
        bindViews();
        presenter = new ISubmitPresenter(this, new ISubmitModel());

        mBtn1.setOnClickListener(this);
        mBtn2.setOnClickListener(this);
        mBtn3.setOnClickListener(this);

        setCache();
    }

    private void setCache() {
        String value = (String) SPUtils.get(KEY_All, "");
        if (!TextUtils.isEmpty(value)) {
            SubmitBean sb = new Gson().fromJson(value, SubmitBean.class);
            if (sb == null) {
                return;
            }
            mEt1.setText(sb.getEt1());
            mEt2.setText(sb.getEt2());
            mEt3.setText(sb.getEt3());
            mEt4.setText(sb.getEt4());
            mEt5.setText(sb.getEt5());
            mEt6.setText(sb.getEt6());
            mEt7.setText(sb.getEt7());
            mEt8.setText(sb.getEt8());
            mEt9.setText(sb.getEt9());
            mEt10.setText(sb.getEt10());
            mEt11.setText(sb.getEt11());
            mEt12.setText(sb.getEt12());
            mEt13.setText(sb.getEt13());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();
    }


    @Override
    public void initSet() {

        mEt1.setText("");
        mEt2.setText("");
        mEt3.setText("");
        mEt4.setText("");
        mEt5.setText("");
        mEt6.setText("");
        mEt7.setText("");
        mEt8.setText("");
        mEt9.setText("");
        mEt10.setText("");
        mEt11.setText("");
        mEt12.setText("");
        mEt13.setText("");
    }

    @Override
    public void tip(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void save() {
        SPUtils.put(KEY_All, new Gson().toJson(getSubmitBean()));
        tip("保存成功!");
    }

    @NonNull
    private SubmitBean getSubmitBean() {
        return new SubmitBean(
                getText(mEt1),
                getText(mEt2),
                getText(mEt3),
                getText(mEt4),
                getText(mEt5),
                getText(mEt6),
                getText(mEt7),
                getText(mEt8),
                getText(mEt9),
                getText(mEt10),
                getText(mEt11),
                getText(mEt12),
                getText(mEt13));
    }


    private String getText(EditText et) {
        return et.getText().toString().trim();
    }

    @Override
    public void onClick(View view) {

        switch (view.getId()) {
            case R.id.btn1:
                presenter.submit(getSubmitBean());
                break;
            case R.id.btn2:
                presenter.initSet();
                break;
            case R.id.btn3:
                presenter.save(getSubmitBean());
                break;
            default:
                break;

        }
    }
}

其他SubmitBean实体类

public class SubmitBean {

    private String et1;
    private String et2;
    private String et3;
    private String et4;
    private String et5;
    private String et6;
    private String et7;
    private String et8;
    private String et9;
    private String et10;
    private String et11;
    private String et12;
    private String et13;

    public String getEt1() {
        return et1;
    }

    public void setEt1(String et1) {
        this.et1 = et1;
    }

    public String getEt2() {
        return et2;
    }

    public void setEt2(String et2) {
        this.et2 = et2;
    }

    public String getEt3() {
        return et3;
    }

    public void setEt3(String et3) {
        this.et3 = et3;
    }

    public String getEt4() {
        return et4;
    }

    public void setEt4(String et4) {
        this.et4 = et4;
    }

    public String getEt5() {
        return et5;
    }

    public void setEt5(String et5) {
        this.et5 = et5;
    }

    public String getEt6() {
        return et6;
    }

    public void setEt6(String et6) {
        this.et6 = et6;
    }

    public String getEt7() {
        return et7;
    }

    public void setEt7(String et7) {
        this.et7 = et7;
    }

    public String getEt8() {
        return et8;
    }

    public void setEt8(String et8) {
        this.et8 = et8;
    }

    public String getEt9() {
        return et9;
    }

    public void setEt9(String et9) {
        this.et9 = et9;
    }

    public String getEt10() {
        return et10;
    }

    public void setEt10(String et10) {
        this.et10 = et10;
    }

    public String getEt11() {
        return et11;
    }

    public void setEt11(String et11) {
        this.et11 = et11;
    }

    public String getEt12() {
        return et12;
    }

    public void setEt12(String et12) {
        this.et12 = et12;
    }

    public String getEt13() {
        return et13;
    }

    public void setEt13(String et13) {
        this.et13 = et13;
    }
    public SubmitBean(){}
    public SubmitBean(String et1, String et2, String et3, String et4, String et5, String et6, String et7, String et8, String et9, String et10, String et11, String et12, String et13) {
        this.et1 = et1;
        this.et2 = et2;
        this.et3 = et3;
        this.et4 = et4;
        this.et5 = et5;
        this.et6 = et6;
        this.et7 = et7;
        this.et8 = et8;
        this.et9 = et9;
        this.et10 = et10;
        this.et11 = et11;
        this.et12 = et12;
        this.et13 = et13;
    }

    @Override
    public String toString() {
        return "{" +
                "et1='" + et1 + '\'' +
                ", et2='" + et2 + '\'' +
                ", et3='" + et3 + '\'' +
                ", et4='" + et4 + '\'' +
                ", et5='" + et5 + '\'' +
                ", et6='" + et6 + '\'' +
                ", et7='" + et7 + '\'' +
                ", et8='" + et8 + '\'' +
                ", et9='" + et9 + '\'' +
                ", et10='" + et10 + '\'' +
                ", et11='" + et11 + '\'' +
                ", et12='" + et12 + '\'' +
                ", et13='" + et13 + '\'' +
                '}';
    }
}

submit.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="提交" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="初始化" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    <EditText
        android:id="@+id/et1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et1" />

    <EditText
        android:id="@+id/et2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et2" />

    <EditText
        android:id="@+id/et3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et3" />

    <EditText
        android:id="@+id/et4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et4" />

    <EditText
        android:id="@+id/et5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et5" />

    <EditText
        android:id="@+id/et6"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et6" />

    <EditText
        android:id="@+id/et7"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et7" />

    <EditText
        android:id="@+id/et8"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et8" />

    <EditText
        android:id="@+id/et9"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et9" />

    <EditText
        android:id="@+id/et10"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et10" />

    <EditText
        android:id="@+id/et11"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et11" />

    <EditText
        android:id="@+id/et12"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et12" />

    <EditText
        android:id="@+id/et13"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="et13" />

    </LinearLayout>
    </ScrollView>
</LinearLayout> 

封装简化mvp

MvpBaseView.class 作为所有View基类

public interface MvpBaseView {
}

MvpBasePresenter.class 业务处理基类

public class MvpBasePresenter<V extends MvpBaseView> {

    private V v;

    public void attachView(V v) {
        this.v= v;
    }

    public void detachView() {
        this.v= null;
    }

    public boolean isViewAttached(){
        return v!= null;
    }

    /**
     * 获取V层
     * @return
     */
    public V getmMvpView() {
        return v;
    }

}

MvpBaseActivity.class mvp中Activity处理

public abstract class MvpBaseActivity<V extends MvpBaseView, P extends MvpBasePresenter<V>> extends AppCompatActivity implements MvpBaseView {

    private P presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(initLayout());
        if (presenter == null) {
            presenter = createPresenter();
        }
        //绑定view
        presenter.attachView((V) this);
        initEvent();
    }

    protected abstract void initEvent();

    protected abstract int initLayout();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除绑定
        if (presenter != null) {
            presenter.detachView();
        }
    }

    /**
     * 创建Presenter
     * @return 子类自己需要的Presenter
     */
    protected abstract P createPresenter();

    /**
     * 获取Presenter
     * @return 返回子类创建的Presenter
     */
    public P getPresenter() {
        return presenter;
    }
}

MvpBaseFragment.class

public abstract class MvpBaseFragment<V extends MvpBaseView, P extends MvpBasePresenter<V>> extends Fragment implements MvpBaseView{

    private P presenter;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view=inflater.inflate(initLayout(),null);
        initEvent();
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (presenter == null) {
            presenter = createPresenter();
        }
        //绑定view
        presenter.attachView((V) this);
    }

    protected abstract void initEvent();

    protected abstract int initLayout();

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //解除绑定
        if (presenter != null) {
            presenter.detachView();
        }
    }

    /**
     * 创建Presenter
     * @return 子类自己需要的Presenter
     */
    protected abstract P createPresenter();

    /**
     * 获取Presenter
     * @return 返回子类创建的Presenter
     */
    public P getPresenter() {
        return presenter;
    }
}

使用mvp之后登录处理

IMode.class 基本不变

public class IMode {

    /**
     * 登录接口
     * @param url
     * @param map
     * @param callback
     */
    public void login(String url, Map map , StringCallback callback){
        OkHttpManager.get(url,map).execute(callback);
    }


    /**
     * 获取当前站点接口
     */
    public void getNowWhere(String url,StringCallback callback){

        OkHttpManager.get(url).execute(callback);

    }
}

MvpBaseView.calss

public interface IView extends MvpBaseView {

    void show();
    void hide();
    void toMainActivity();
    void tip(String msg);


}

LoginActivity.class

public class LoginActivity extends MvpBaseActivity<IView,IPresenter> implements IView,View.OnClickListener{

    private ProgressDialog progressDialog;
    private EditText et_name;
    private EditText et_password;
    private Button btn;


    @Override
    protected IPresenter createPresenter() {
        return new IPresenter(this,new IMode());
    }

    @Override
    protected void initEvent() {
        et_name=findViewById(R.id.username);
        et_password=findViewById(R.id.password);

        btn=findViewById(R.id.btn);
        btn.setOnClickListener(this);

        progressDialog=new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在请求...");
    }

    @Override
    protected int initLayout() {
        return R.layout.activity_main;
    }

    @Override
    public void show() {
        progressDialog.show();
    }

    @Override
    public void hide() {
        progressDialog.hide();
    }

    @Override
    public void toMainActivity() {
        startActivity(new Intent(this, HomeActivity.class));
    }

    @Override
    public void tip(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }



    @Override
    public void onClick(View view) {

        switch (view.getId()){

            case R.id.btn:
                String name=et_name.getText().toString().trim();
                String password=et_password.getText().toString().trim();

                getPresenter().login(name,password);

                break;

                default:
                    break;
        }
    }
}

其实也就是省去注销P以及约束V…

代码下载

Demo

参考文档
1、参考一 Android MVP架构搭建
2、参考二 一个小例子彻底搞懂 MVP