MVP

1.常见项目架构模型

  • 你是否遇到过Activity/Fragment中成百上千行代码,完全无法维护,看着头疼?
  • 你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况?
  • 你是否遇到过用MVC架构写的项目进行单元测试时的深深无奈?
  • 开发的时候一般都会使用一些架构,好处就是代码逻辑清晰,将第模块之间的耦合,方便测试,维护;
    常见的的架构:MVC、MVP、MVVM

2.MVC架构

①MVC概述

  • MVC框架模式最早由Trygve Reenskaug 于1978年在Smalltalk-80系统上首次提出。经过了这么多年的发展,当然会演变出不同的版本,但核心没变依旧还是三层模型Model-View-Control。
  • 可能由于MVP、MVVM的兴起,MVC在android中的应用变得越来越少了,但MVC是基础,理解好MVC才能更好的理解MVP,MVVM。因为后两种都是基于MVC发展而来的。
mvc.jpg

得出:从上图可以看出M层和V层有连接关系,而Activity有时候既充当了控制层又充当了视图层,导致项目维护比较麻烦.

  • 视图层(View)
    对应于xml布局文件和java代码动态view部分

  • 控制层(Controller)
    MVC中Android的控制层是由Activity来承担的,Activity本来主要是作为初始化页面,展示数据的操作,但是因为XML视图功能太弱,所以Activity既要负责视图的显示又要加入控制逻辑,承担的功能过多。

  • 模型层(Model)
    针对业务模型,建立的数据结构和相关的类,它主要负责网络请求,数据库处理,I/O的操作。

由于android中有个god object的存在activity,再加上android中xml布局的功能性太弱,所以activity承担了绝大部分的工作。因为activity扮演了controller和view的工作,所以controller和view不太好彻底解耦,但是在一定程度上我们还是可以解耦的。

②MVC架构优缺点

  • A. 缺点

M层和V层有连接关系,没有解耦,导致维护困难.
Activity/Fragment中的代码过多,难以维护.
Activity中有很多关于视图UI的显示代码,因此View视图和Activity控制器并不是完全分离的,当Activity类业务过多的时候,会变得难以管理和维护.尤其是当UI的状态数据,跟持久化的数据混杂在一起,变得极为混乱.

  • B. 优点

控制层和View层都在Activity中进行操作,数据操作方便.
模块职责划分明确.主要划分层M,V,C三个模块.

③MVC总结

  • 具有一定的分层,model彻底解耦,controller和view并没有解耦

  • 层与层之间的交互尽量使用回调或者去使用消息机制去完成,尽量避免直接持有

  • controller和view在android中无法做到彻底分离,但在代码逻辑层面一定要分清

  • 业务逻辑被放置在model层,能够更好的复用和修改增加业务

3.MVP架构

mvp.jpg

①概述

MVP,即是Model,View,Presenter架构模式.看起来类似MVC,其实不然.从上图能看到Model层和View层没有相连接,完全解耦.

②分层

  • M层:模型层(Model),此层和MVC中的M层作用类似.主要是实体类,数据库,网络等存在的层面

  • V层:视图层(View),在MVC中V层只包含XML文件,而MVP中V层包含XML,Activity和Fragment三者.理论上V层不涉及任何逻辑,只负责界面的改变,尽量把逻辑处理放到M层.

  • P层:通知层(Presenter),P层的主要作用就是连接V层和M层,起到一个通知传递数据的作用.

③原理

用户触碰界面触发事件,View层把事件通知Presenter层,Presenter层通知Model层处理这个事件,Model层处理后把结果发送到Presenter层,Presenter层再通知View层,最后View层做出改变.这是一整套流程.

④. MVP架构优缺点

  • A. 缺点

MVP中接口过多.
每一个功能,相比于MVC要多写好几个文件.
如果某一个界面中需要请求多个服务器接口,这个界面文件中会实现很多的回调接口,导致代码繁杂.
如果更改了数据源和请求中参数,会导致更多的代码修改.
额外的代码复杂度及学习成本.

  • B. 优点

模块职责划分明显,层次清晰,接口功能清晰.
Model层和View层分离,解耦.修改View而不影响Model.
功能复用度高,方便.一个Presenter可以复用于多个View,而不用更改Presenter的逻辑.
有利于测试驱动开发,以前的Android开发是难以进行单元测试.
如果后台接口还未写好,但已知返回数据类型的情况下,完全可以写出此接口完整的功能.

四.MVP架构实战

①MVP简单案例

用户点击按钮后,Presenter层通知Model层请求处理网络数据,处理后Model层把结果数据发送给Presenter层,Presenter层再通知View层,然后View层改变TextView显示的内容.

②布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".view.SingleInterfaceActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100px"
        android:text="请点击上方按钮获取数据" />
</LinearLayout>

③页面:

public class SingleActivity extends AppCompatActivity {

    private Button button;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
}

④Api:

public interface MyServer {

    public String Url = "http://api.shujuzhihui.cn/api/news/";

    @GET("categories?")
    Observable<ReBean> getData(@Query("appKey") String appKey);
}

⑤bean:

public class ReBean {
    private String ERRORCODE;
    private List<String> RESULT;

    public String getERRORCODE() {
        return ERRORCODE;
    }

    public void setERRORCODE(String ERRORCODE) {
        this.ERRORCODE = ERRORCODE;
    }

    public List<String> getRESULT() {
        return RESULT;
    }

    public void setRESULT(List<String> RESULT) {
        this.RESULT = RESULT;
    }
}

⑥Model:

public interface MyModel {

    void getData(String appKey, CallBack<ReBean, String> callBack);
}

public class MyModelImpl implements MyModel {

    @Override
    public void getData(String appKey, final CallBack<ReBean, String> callBack) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MyServer.Url)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        MyServer myServer = retrofit.create(MyServer.class);

        Observable<ReBean> data = myServer.getData(appKey);

        data.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ReBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ReBean reBean) {

                        //成功
                        //txt.setText(reBean.getRESULT().toString());
                        if (reBean!=null){
                            if (reBean.getERRORCODE().equals("0")){
                                callBack.onSuccess(reBean);
                            }else{
                                callBack.onFail("失败");
                            }
                        }else{
                            callBack.onFail("出现错误");
                        }
                    }

                    @Override
                    public void onError(Throwable e) {

                        //失败
                        callBack.onFail("出现错误");
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

}

⑥CallBack:

public interface CallBack<K, V> {
    void onSuccess(K data);

    void onFail(V data);
}

⑦Presenter:

public interface MyPresenter {

    void getData(String appKey);
}

public class MyPresenterImpl implements  MyPresenter, CallBack<ReBean,String> {

    private MyModel myModel;
    private MyView myView;

    public MyPresenterImpl(MyModel myModel,MyView myView) {
        this.myModel = myModel;
        this.myView = myView;
    }

    @Override
    public void getData(String appKey) {
        if (myModel!=null){
            myModel.getData(appKey, this);
        }
    }

    @Override
    public void onSuccess(ReBean data) {
        //如果Model层请求数据成功,则此处应执行通知View层的代码   
        if(myView!=null){
            myView.onSuccess(data);
        }
    }

    @Override
    public void onFail(String data) {
        //如果Model层请求数据失败,则此处应执行通知View层的代码
        if(myView!=null){
            myView.onFail(data);
        }
    }

}

⑧IView:

public interface MyView<K,V> {

    void onSuccess(K data);

    void onFail(V data);
}


public class MainActivity extends AppCompatActivity implements View.OnClickListener, MyView<ReBean,String> {

    private Button btn;
    private TextView txt;
    private String appKey = "908ca46881994ffaa6ca20b31755b675";
    private MyPresenterImpl myPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myPresenter = new MyPresenterImpl(new MyModelImpl(), this);

        initView();
    }

    private void initView() {
        btn = (Button) findViewById(R.id.btn);
        txt = (TextView) findViewById(R.id.txt);

        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn:
                initData();
                break;
        }
    }

    private void initData() {
        myPresenter.getData(appKey);
    }

    @Override
    public void onSuccess(ReBean data) {
        txt.setText(data.getRESULT().toString());
    }

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

推荐阅读更多精彩内容