Android MVP模式

Android MVP模式

  • View 对应于Activity,负责View的绘制以及与用户交互
  • Model 依然是业务逻辑和实体模型
  • Presenter 负责完成View于Model间的交互

mvp模式简化的Activity的代码逻辑,将复杂的逻辑代码提取到了Presenter中进行处理。对应的耦合度降低。

一张图总结Mvc和Mvp的区别

20150622212916054.png

其实最明显的区别就是,MVC中中Model和View直接交互的,而MVP中很明显,Model与View之间的交互由Presenter完成,Presenter与View之间的交互是通过接口的。

Android Demo

需求:页面上有一个按钮,点击后去请求数据,成果后改变页面显示。

不使用mvp:

public class NotMvpActivity extends AppCompatActivity {

    private Button bt;
    private TextView tv;

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

        bt = (Button) findViewById(R.id.bt);
        tv = (TextView) findViewById(R.id.tv);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                requestData();
            }
        });

    }

    /**
     * 获取数据
     */
    private void requestData() {

        tv.setText("加载中");

        new Thread() {
            @Override
            public void run() {
                super.run();

                SystemClock.sleep(2000);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText("成功获取到数据");
                    }
                });

            }
        }.start();

    }
}

以上应该是最原始的方法,下面使用mvp改造。

步骤:

1、创建接口,定义页面可能会出现的情况(加载中,成功,失败)。

2、Activity实现接口,成功View层。

3、创建一个类,实现网络请求的操作,Model层。

4、创建一个类,处理Model和View之间的通信,Presenter层。

1、view层的接口:

/**
 * @project:MvpDemo
 * @author:小卷子
 * @date 2018/6/29
 * @describe:View层接口 定义页面可能会出现的情况(加载中,成功,失败)
 * @fix:
 */
public interface DemoView {

    /**
     * 加载中
     */
    void Loading();
    /**
     * 获取成功
     */
    void getDataSuccess();
    /**
     * 获取失败
     */
    void getDataFail();
}

2、让Activity实现这个接口中的方法

public class DemoAcitivity extends AppCompatActivity implements DemoView {


    private Button bt;
    private TextView tv;

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

        bt = (Button) findViewById(R.id.bt);
        tv = (TextView) findViewById(R.id.tv);

    }

    @Override
    public void Loading() {
        tv.setText("加载中");
    }

    @Override
    public void getDataSuccess() {
        tv.setText("成功");
    }

    @Override
    public void getDataFail() {
        tv.setText("失败");
    }
}

3、创建一个类,用来封装之前的网络请求。(DemoModelCallBack是网络请求的回调,demo只是简单模拟)

/**
 * @project:MvpDemo
 * @author:小卷子
 * @date 2018/6/29
 * @describe:Mode1层 请求数据
 * @fix:
 */
public class DemoMode1 {
    
    public void getData(final DemoModelCallBack callBack) {

        new Thread() {
            @Override
            public void run() {
                super.run();
                SystemClock.sleep(2000);

                if (callBack != null) {
                    callBack.getDataSuccess();
                }
            }
        }.start();
    }

}

4、处理Model和View之间的通信,Presenter

/**
 * @project:MvpDemo
 * @author:小卷子
 * @date 2018/6/29
 * @describe:Presenter处理v和m的通信
 * @fix:
 */
public class DemoPresenter {

    private DemoModel demoModel;
    private DemoView demoView;

    public DemoPresenter(DemoModel demoModel, DemoView demoView) {
        this.demoModel = demoModel;
        this.demoView = demoView;
    }

    /**
     * 请求数据
     */
    public void getData() {
        //view处理请求中
        demoView.Loading();
        //去请求数据
        demoMode1.getData(new DemoModelCallBack() {
            @Override
            public void getDataSuccess() {
                demoView.getDataSuccess();
            }

            @Override
            public void getDataFail() {
                demoView.getDataFail();
            }
        });
        
    }
}

5、最后调整一下Activity ,Activity处理需要实现View层的接口,还要在自身创建Persenter。Activity接收到用户的事件时全部交给Persenter来处理。Persenter处理事件后将结果反馈到View(也就是Activity)。所以思路大概是这样:

  • Activity需要实现View层接口
  • Persenter需要持有View层引用和Model层引用
  • 在Activity中创建Persenter,通过Persenter连接自身和Model

调整后的Activity

/**
 * @project:MvpDemo
 * @author:小卷子
 * @date 2018/6/29
 * @describe:Activity实现view接口
 * @fix:
 *
 */

public class DemoAcitivity extends AppCompatActivity implements DemoView {


    private Button bt;
    private TextView tv;
    private DemoPresenter demoPresenter;

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

        bt = (Button) findViewById(R.id.bt);
        tv = (TextView) findViewById(R.id.tv);

        demoPresenter = new DemoPresenter(this);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击事件交给 demoPresenter处理
                demoPresenter.getData();
            }
        });

    }




    @Override
    public void Loading() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.setText("加载中");
            }
        });
    }

    @Override
    public void getDataSuccess() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.setText("成功");
            }
        });
    }

    @Override
    public void getDataFail() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.setText("失败");
            }
        });
    }
}

简单的Demo模式的MVP架构存在很多问题不能应用到实际开发中。(代码冗余,复用性差)。所以针对这些问题需要进行优化。

调用View可能引发的空指针异常

以上例子中请求网络数据等待反馈后跟新到页面,但是在请求过程中当前的View可能被销毁,Presenter在网络回调后调用View更新就会引发空指针。要避免这种情况就要在每次调用View的时候都需要知道View是否存在。也就是Presenter必须知道Activity的生命状态。

Presenter中新增:

    /**
     * 绑定
     *
     * @param demoView
     */
    public void attachView(DemoView demoView) {
        this.demoView = demoView;
        this.demoModel = new DemoModel();
    }

    /**
     * 解除绑定
     */
    public void detachView() {
        this.demoView = null;
        //todo DemoModel取消网络请求
    }


    /**
     * 当前是否与View建立连接
     * 每次调用view的时候先调用此方法检查
     * @return
     */
    public boolean isViewAttached() {
        return demoView != null;
    }
  • attachView() 绑定View引用。
  • detachView 断开View引用。
  • isViewAttached() 判断View引用是否存在。

Presenter与View的绑定不在通过构造传入,通过attachView()detachView 绑定生命周期,每次调用View之前判断是否存在。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容