MVP google官方demo比较分析

作者: 大于于 | 来源:发表于2016-05-29 14:42 被阅读15924次

15年年底本人公司新开的一个项目,用上了mvp模式开发,那个时候还没发现google出了mvp的demo。

首先什么是MVP:

  • M-model,即javaBean 数据模型层;
  • V-view,视图层,常用的即Activity Fragment,这里是定义一个接口IView,Activity去实现IView的写法;
  • P-presenter,数据处理层,所有的数据逻辑,业务逻辑都在这里处理;

原来我的写法

而当时我在写mvp时只是简单的写成了:以下几个类:
  • UserInfoModel-model;
  • IUserInfoView-IView;
  • UserInfoActivity-Activity;
  • UserInfoPresenter-Presenter;
接下来假设业务是这样的:网络请求用户信息接口,并将用户信息展现在UserInfoActivity中。
  • 先看目录结构:


    目录.png
  • 1:先看Model--UserInfoModel

public class UserInfoModel {
    private String name;
    private int age;
    private String address;
    public UserInfoModel(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}
  • 2:View--实现的IUserInfoView:
public interface IUserInfoView {   
          String loadUserId();//假设接口请求需要一个userId
          void showLoading();//展示加载框
          void dismissLoading();//取消加载框展示
          void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调
}

网络接口请求用户信息之前 获得userId,然后展示loading,数据加载成功取消loading,最后将数据展示在Activity上

  • 3:Presetner--UserInfoPresenter:这里就实现一个模拟的接口请求
public class UserInfoPresenter {
    private IUserInfoView iUserInfoView;

    public UserInfoPresenter(IUserInfoView iUserInfoView) {
        this.iUserInfoView = iUserInfoView;
    }

    public void loadUserInfo() {
        String userId = iUserInfoView.loadUserId();
        iUserInfoView.showLoading();//接口请求前显示loading
        //这里模拟接口请求回调-
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //模拟接口返回的json,并转换为javaBean
                UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");
                iUserInfoView.showUserInfo(userInfoModel);
                iUserInfoView.dismissloading();
            }
        }, 3000);
    }
}
  • 4:View--UserInfoActivity实现IUserInfoView接口:
public class UserInfoActivity extends AppCompatActivity implements IUserInfoView {
    private TextView tv_name;
    private TextView tv_age;
    private TextView tv_address;
    private UserInfoPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_name = (TextView) findViewById(R.id.tv_name);
        tv_age = (TextView) findViewById(R.id.tv_age);
        tv_address = (TextView) findViewById(R.id.tv_address);
        presenter = new UserInfoPresenter(this);
        presenter.loadUserInfo();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this, "正在加载", Toast.LENGTH_LONG).show();
    }

    @Override
    public void dismissLoading() {
        Toast.makeText(this, "加载完成", Toast.LENGTH_LONG).show();
    }

    @Override
    public void showUserInfo(UserInfoModel userInfoModel) {
        if (userInfoModel != null) {
            tv_name.setText(userInfoModel.getName());
            tv_age.setText(String.valueOf(userInfoModel.getAge()));
            tv_address.setText(userInfoModel.getAddress());
        }
    }

    @Override
    public String loadUserId() {
        return "1000";//假设需要查询的用户信息的userId是1000
    }
}

这样写并没有错,只是不能更直观的看到IView中的方法和Presenter中的方法的关联。

Google demo写法有所不同:google官方mvp写法demo

下面用google官方demo的写法实现上面的模拟业务:
  • 首先看下目录结构:

google demo mvp 目录.png

这里多了一个contract包:里面放的是契约接口。更能直接明了的看到View和Presenter之间的方法。
还多了一个BaseView,BasePresenter:看代码

public interface BasePresenter {
    void start();
}

这里的start()方法就相当于约定了所有的Presenter的初始化操作都放在start()方法中;

public interface BaseView<T> {
    void setPresenter(T presenter);
}
  • 再来看契约类:UserInfoContract
public interface UserInfoContract {
    interface View extends BaseView<Presenter>{
        void showLoading();//展示加载框
        void dismissLoading();//取消加载框展示
        void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调
        String loadUserId();//假设接口请求需要一个userId
    }
    interface Presenter extends BasePresenter {
        void loadUserInfo();
    }
}

契约内部有2个接口,分别继承了BaseView和BasePresenter,View和Presenter中实现的方法分别是UI操作,和数据业务逻辑操作,此时是不是看的异常的清晰。

多了一个契约类,契约内部包含了2个接口,一个是Presenter一个是View,就相当于之前的写法中的接口IView和普通类Presenter,只不过现在都将这两个类所需要的业务和UI层的接口直接放在一起展现出来,变得很清晰。在契约接口中的Presenter是一个接口,需要我们去实现,代码如下:

public class UserInfoPresenter implements UserInfoContract.Presenter {
    private UserInfoContract.View view;

    public UserInfoPresenter(UserInfoContract.View view) {
        this.view = view;
        view.setPresenter(this);
    }

    @Override
    public void loadUserInfo() {
        String userId = view.loadUserId();
        view.showLoading();//接口请求前显示loading
        //这里模拟接口请求回调-
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //模拟接口返回的json,并转换为javaBean
                UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");
                view.showUserInfo(userInfoModel);
                view.dismissLoading();
            }
        }, 3000);
    }

    @Override
    public void start() {
        loadUserInfo();
    }
}

1:UserInfoPresenter 构造函数中传入UserInfoContract.View,并且调用view的setPresenter()方法;
2:将所有的初始化操作都放在start()方法中(这里demo只有一个:网络请求获取用户信息),这样只要进入界面的时候调用start()方法就可以执行一系列初始化的操作,这就相当于一种约定好的开发。

  • 最后看UserInfoActivity如何进行调用
public class UserInfoActivity extends AppCompatActivity implements UserInfoContract.View {
    private TextView tv_name;
    private TextView tv_age;
    private TextView tv_address;

    private UserInfoContract.Presenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_name = (TextView) findViewById(R.id.tv_name);
        tv_age = (TextView) findViewById(R.id.tv_age);
        tv_address = (TextView) findViewById(R.id.tv_address);

        new UserInfoPresenter(this);
        presenter.start();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this, "正在加载", Toast.LENGTH_LONG).show();
    }

    @Override
    public void dismissLoading() {
        Toast.makeText(this, "加载完成", Toast.LENGTH_LONG).show();
    }

    @Override
    public void showUserInfo(UserInfoModel userInfoModel) {
        if (userInfoModel != null) {
            tv_name.setText(userInfoModel.getName());
            tv_age.setText(String.valueOf(userInfoModel.getAge()));
            tv_address.setText(userInfoModel.getAddress());
        }
    }

    @Override
    public String loadUserId() {
        return "1000";//假设需要查询的用户信息的userId是1000
    }

    @Override
    public void setPresenter(UserInfoContract.Presenter presenter) {
        this.presenter = presenter;
    }
}

在onCreate()方法:

new UserInfoPresenter(this);
presenter.start();

而并没有写成

presenter=new UserInfoPresenter(this);

因为UserInfoActivity实现了UserInfoContract.View中的setPresenter()方法;而UserInfoPresenter 构造函数中已经调用了UserInfoContract.View中的setPresenter()方法;

两者思想一样,只是写法不同。
后者demo地址

相关文章

网友评论

  • 姑娘请别为难小僧:请教下直接new UserInfoPresenter 赋值,跟回调设置有什么好处?
    02bbd464ac8c:同问,望作者能解答
  • b07643cf751e:刚好要用,支持一波,楼主无敌
  • Bo动:传过去的this可能引起内存泄漏
  • Bo动:请问为什么不直接写成:presenter=new UserInfoPresenter(this);这样还省下几行代码
  • 小白咸菜:大佬,Model我还是没怎么理解。。。
  • 1cf651476639:写的很清楚明白,学到很多东西,谢谢楼主!
  • CokeNello:楼主,后面,Model层呢?
  • 2f5ae444315b:谷歌原生的其实就把view的接口方法和presenter 接口方法结合了 多了一个这样的契约接口类UserInfoContract,使人一看就知道view都要做什么 业务逻辑都要做什么,整个项目我感觉不要全用mvp模式架构,反而会增加开发时间,而且类会很多。如果有的页面功能复杂代码量太大超过2000行吧 整个页面的开发可以采用mvp,而不是整个项目采用mvp。
  • b4c1f35087ee:咋看都是一回事- -!
  • 我like砂糖:写得不错,赞:+1:
  • 吧主:这篇文章,我给你在公众号原创发布可以吗?公众号:杨守乐
    大于于: @吧主 原创发布?
  • db1e9a213ac9:我来说下我的感觉吧,不知道对不对
    View就专门做显示数据的,如果需要有逻辑处理,直接丢给UserInfoContract.View中的方法处理,我(代指View)不关心,处理完后,将结果(userInfoModel)告诉我(View),我来在界面上(各个控件)显示出来。

  • 坐槐不乱:谷歌为什么采用setPresenter()方法进行v与p的关联,有什么不一样吗
    姬94:我感觉,,吧,,你应该看看名字,,setPresenter ,就是设置,,Presenter的名称,已经说的很明白了。我要处理业务逻辑了。关键,P的意思是业务逻辑,这一点你要明白,我感觉你想问的 是,为什么,是BaseView<T> 这个泛型吧,这个框架的根的意思,任何想实现,P最好都有一个统一的出发点,这不就是抽象、封装的意思吗?
  • fbcde8a99e17:确实更清晰了
  • d8184ca3c970:求教谷歌这个demo链接进去怎么下载
    四无小青年:右边有个绿色的按钮,你点击试试
  • 菜鸟考官:写的非常好。 很感谢。
  • 卜俊文:看了很多的MVP的教程,要么就是太复杂,要么就是过时的感觉,一直没懂怎么个写法,看到你这一比较,确实清晰很多。3q~
  • xyyou123:请教 为什么要将presenter 里面的所有代码都封装在 start()方法里面?
    db1e9a213ac9:我的理解是,初始化时,只需要一个初始化入口,这里start()方法只进行了一个loadUserInfo()初始化。如果还有其它的方法需要,也可以再在start() 中加入
    大于于: @xyyou123 建议刚进入界面就需要初始化的一些操作在start方法中调用
  • 男子汉大豆腐:很赞,很清晰,还有比较,小白表示看懂了 :kissing_closed_eyes:
  • ibrucekong:请问web和app在MVP上一样嘛,能不能发个web的MVP的demo!
    姬94:web 可以看大神,http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html 阮一峰的网络日志 他的博客,他写web大神
    大于于:@ibrucekong sorry!😲
  • f49cef042ed7:现在有各种对Google出示的MVP解说,但很少将以前项目与官方项目进行比对,或许你给的原来项目也是个demo,但已经很不错了,能更直观的区别官网与自己所想
    大于于:@Magier 两种写法都在实际项目中开发过。
  • ce1dd2756b0f:写的很好~赞 :relaxed:
    大于于:@skylineTan 😊
  • 帅气小伙:谷歌官方的东西要有一定水平才能理解,对小白不和谐
    81e201da81b9:@帅气小伙 建议还是从基础开始比较好。
    f49cef042ed7:@帅气小伙 我觉得架构理念都是通用,不仅仅是app web也是如此,不管是不是小白,理解其设计理念是很重要的,在其次是自己个人编程能力水平,我也没参与过实际的android项目开发。。。哈哈 有压力才有进步……:relieved:我说的话都一套一套的……
    大于于:@帅气小伙 哈哈,所以贴了两种代码,不过谁不是从小白过来的呢。
  • 东风四幺:不错😊
    大于于:@东风四幺 😄

本文标题:MVP google官方demo比较分析

本文链接:https://www.haomeiwen.com/subject/yahqdttx.html