Base封装之我的最简MVP架构

绪论

最近懒癌症犯了,好久都没写博客了,当然也在做一些东西,也在整理自己一直以来使用的一些技术点,从Retrofit到OkGO,从ListView到RecycleView,从Java到Kotlin….总之一直在尝试新的技术,今天分享一下自己一直所用的MVP,整理完了分享给大家,有不合适或者不正确的地方还希望大家多多指正,共同交流。

对了 打一波广告 我的新的个人博客 http://hankkin.cn/

好了接下来开始我们的MVP

背景

众所周知 MVP这种架构模式已经出现很久了,大体时间应该是2014年吧,现在网上的关于MVP的文章也很多,各式各样的关于MVP的架构知识都涌现出来,可想而知现在这种架构有多么火,还有目前风头正劲的MVVM,当然我并不觉得我现在写MVP有些晚,因为每个人都有每个人的架构,每个人都可以根据自己的逻辑封装出来自己的架构模式,今天我介绍的便是我自己通过项目总结出来的MVP

什么是MVP

MVP知识点

MVP - Model-View-Presenter

MVP和MVC的区别仅仅在于P和Control,MVC中View和Model是互通的可以互相通信,在Android中View一般代表着我们的xml进行界面的描述,而对于模型Model部分则大多对应于本地的数据文件或网络获取的数据体,很多情况下我们对这些数据的处理也会在这一层中进行,最后的控制器Controller则当之无愧的是右Activity承担。

而MVP中view通过presenter访问model,大大的减小了耦合性,业务逻辑都交给P处理,通过P访问V层更改UI。MVP模式可以分离显示层与逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。

这里不多介绍MVC了,相信大家都很熟悉

MVP的优缺点

优点:

  • 降低耦合度,实现了M层和V层的完全分离,可以修改V层不影响M层
  • 模块职责划分明显,层次清晰
  • P层可以复用,一个P可以对应多个V,不需要修改P的逻辑
  • 单元测试更加简单方便
  • 代码灵活度高

缺点:

  • V层和P层交互频繁
  • 代码量多,类变多了

总结

  • M层负责存储、检索、操纵数据,代表着一类组件或者类,这些组件或类可以向外部提供数据,同时也能从外部获取数据将数据存储起来
  • V层负责将数据UI呈现给用户。一般的视图UI只包含界面,并不包含界面逻辑,V层收P层控制,在Android中一般是Activity、Fragment、View、ViewGroup。。。
  • P层作为V层和M层的中间枢纽,处理用户交互的业务逻辑

MVP实现

1.基本实现

我们都知道一般MVP架构一共需要以下四步:

  • 定义一个interface接口XView,对应的Activity,Fragment实现这个interface
  • 编写Molde,里面的业务逻辑主要包括网络请求获取数据,数据库读取等耗时操作,通过M层回调给P层通知V层更新UI
  • 编写Presenter,P层持有V和M的引用,实现P层的回调,并且回调给V层更新
  • Activity中调用P执行业务逻辑,更新UI

具体代码就不贴了,相信了解过MVP的都会写基本的代码

但是问题也就出来了,由于P层需要和V层进行通信,更新UI时需要持有V层的view对象,那么我们每个P里面一般都用构造去初始化这个View,类多了之后感觉很烦,而View层里的一些常用的方法我们也可以封到base里面,比如loading的显示隐藏,空布局和错误布局的显示…

2.Base封装

1.BaseView
package com.hankkin.xlibrary.mvp;import android.view.View;
/**
 * Created by hankkin on 2017/3/29.
 */public interface BaseView {

    /**
     * 显示loading框
     */
    void showProgress();

    /**
     * 隐藏loading框
     */
    void hideProgress();

    void toast(CharSequence s);

    void toast(int id);

    void toastLong(CharSequence s);

    void toastLong(int id);


    /**
     * 显示空数据布局
     */
    void showNullLayout();

    /**
     * 隐藏空数据布局
     */
    void hideNullLayout();

    /**
     * 显示异常布局
     * @param listener
     */
    void showErrorLayout(View.OnClickListener listener);

    void hideErrorLayout();
}
2.BasePresenter
package com.hankkin.xlibrary.mvp;
/**
 * Created by hankkin on 2017/3/29.
 */public abstract class BasePresent<T>{
    public T view;

    public void attach(T view){
        this.view = view;
    }

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

我们在BasePresenter里面去初始化View对象,同时提供释放View对象以防止内存溢出

3.MvpActivity
package com.hankkin.hlibrary.base;
import android.os.Bundle;
import android.support.annotation.Nullable;

import com.lzy.okgo.OkGo;/**
 * Created by hankkin on 2017/3/29.
 */public abstract class MvpActivity<V,P extends BasePresent<V>> extends BaseAcitvity{

    protected P presenter;



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = initPresenter();
    }

    @Override
    protected void onResume() {
        super.onResume();
        presenter.attach((V) this);
    }

    @Override
    protected void onDestroy() {
        presenter.detach();
        OkGo.getInstance().cancelTag(this);
        super.onDestroy();
    }

    public abstract P initPresenter();}

这样我们在Activity中初始化P,并且连接V,在onDestroy()生命周期中释放P中引用的V。

Example

我们按照功能模块来构造我们的MVP,可能大家注意到了没有M层啊,是的,这里我把M层舍弃掉了,把业务逻辑、网络请求直接放在了P层,大大减少了类的数量,这样我们每个功能模块只需要新建一个View和一个Presenter就可以满足了,特殊的需求再通过特殊方法来处理,下面我们举一个简单的例子:

网络请求我用的 jeasonlzy 大神的OKGo3,刚出锅没几天,尝试一下,个人认为封装的非常非常好,继承了Rx,Retrofit,相信你会喜欢的。

https://github.com/jeasonlzy/okhttp-OkGo

好了下面看我们的例子吧:

项目结构

看一下项目结构

HomeView

我用的Gank.io里面的一个接口获取数据,首先我们定义我们的HomeView,里面有两个方法获取数据成功和获取失败

package com.hankkin.mvpdemo.home;
import com.hankkin.hlibrary.BaseView;
/**
 * Created by hankkin on 2017/6/19.
 */public interface HomeView extends BaseView{

    void getDataHttp(String data);

    void getDataHttpFail(String msg);
}
HomePresenter

然后我们定义HomePresenter,里面只有我们的网络请求,因为我们的BasePresenter持有View对象,所以在回调中直接调用HomeView的两个成功失败的方法

package com.hankkin.mvpdemo.home;
import com.hankkin.hlibrary.BasePresent;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.model.Response;
/**
 * Created by hankkin on 2017/6/19.
 */public class HomePresenter extends BasePresent<HomeView> {


    public void getGankData(){
        OkGo.<String>get("http://gank.io/api/data/Android/10/1")
                .tag(this)
                .execute(new StringCallback() {
                    @Override
                    public void onSuccess(Response<String> response) {
                        view.getDataHttp(response.body());
                    }

                    @Override
                    public void onError(Response<String> response) {
                        super.onError(response);
                        view.getDataHttpFail(response.message());
                    }
                });
    }
}
Activity

最后看一下Activity,我们的Activity继承了MVPActivity并实现了HomeView,同时将泛型对象设为我们的HomeView和HomePresenter,这样我们就可以直接调用P层的网络请求方法,同时也能回调更新UI

package com.hankkin.mvpdemo;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.view.MenuItem;import android.view.View;
import android.widget.Button;import android.widget.TextView;
import com.hankkin.hlibrary.MvpActivity;
import com.hankkin.mvpdemo.home.HomePresenter;
import com.hankkin.mvpdemo.home.HomeView;
import static com.hankkin.mvpdemo.R.id.btn_get;
public class MainActivity extends MvpActivity<HomeView,HomePresenter> implements HomeView{

    private TextView mTextMessage;
    private Button btnGet;

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    mTextMessage.setText(R.string.home);
                    return true;
                case R.id.navigation_dashboard:
                    mTextMessage.setText(R.string.control);
                    return true;
                case R.id.navigation_notifications:
                    mTextMessage.setText(R.string.notification);
                    return true;
            }
            return false;
        }
    };

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

        mTextMessage = (TextView) findViewById(R.id.message);
        btnGet = (Button) findViewById(btn_get);
        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showProgress();
                presenter.getGankData();
            }
        });
    }

    @Override
    public HomePresenter initPresenter() {
        return new HomePresenter();
    }


    @Override
    public void getDataHttp(String data) {
        mTextMessage.setText(data);
        hideProgress();
    }

    @Override
    public void getDataHttpFail(String msg) {
        toast(msg);
    }


    @Override
    public void toast(CharSequence s) {
        toast("获取成功");
    }
}

结论

对于BaseActivity我在之前的文章里面已经介绍了,还不了解的请看

Android谈谈封装那些事–BaseActivity和BaseFragment(一)

Android谈谈封装那些事–BaseActivity和BaseFragment(二)

也已经优化过了相关的封装逻辑,也会在接下来的文章继续介绍的。

下一篇文章我会继续介绍我的封装之路,近期会将我的HLibrary提到我的Github上,大家可以star一下我的Github。

代码已经上传到我的Github

https://github.com/Hankkin/MvpDemo

好了是不是很简单呢?小伙伴们如果有啥好的建议或者觉得不妥的地方希望及时指正,共同交流,谢谢。

其实MVP有好多种,这里给大家推荐几个我觉得比较好的

http://www.jianshu.com/p/3a17382d44de#

http://www.jianshu.com/p/9a6845b26856

一步一步实现Android的MVP框架

原文发布于微信公众号 - 非著名程序员(non-famous-coder)

原文发表时间:2017-06-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货园

Base封装(一)--我的最简MVP架构

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/73...

16010
来自专栏分享达人秀

Fragment使用起来非常简单

Fragment创建完成后并不能单独使用,还需要将Fragment加载到Activity中,在Activity中添加Fragment的方式有两种:静态加...

35740
来自专栏haifeiWu与他朋友们的专栏

Dagger Android支持库(译文)

与其他依赖注入框架相比,Dagger 2 最大的优点是他不使用反射,严格的生成实现类,这意味着他可以使用在 Android 应用上。但是在Android上使用仍...

12130
来自专栏分享达人秀

引入Fragment原来是这么回事

随着大众生活水平的提高,再加上移动互联网的迅速发展,几乎每个人都至少拥有一台搭载Android系统的移动设备。Android设备的多样性给我们带来了很大...

37650
来自专栏分享达人秀

ListView列表数据源——Adapter

在上一节一起了解了ListView的简单使用,那么本节继续来学习与ListView有着千丝万缕的Adapter。 一、了解MVC模式 在开始学习...

424100
来自专栏Android先生

(新瓶旧酒)谷歌官方MVP项目学习--浅入源码

项目的目的是通过展示各种架构app的不同方式来帮助开发者解决架构问题。项目中通过不同的架构概念及方式实现了功能相同的app。你可以用示例来当做参考,或是干脆拿来...

13210
来自专栏jianhuicode

Fragment 学习笔记(1)

网上关于Fragment相关的博客资料很多,写关于这个知识笔记是加深记忆,大神略过; 0x01 了解Fragment 当然看官方文档(http://www.an...

19950
来自专栏刘望舒

让 Android 开发像 web 开发一样爽

作者:hanks https://juejin.im/post/5bf12c8751882511a8527ed4

13020
来自专栏懒人开发

BottomNavigationView简单使用

之前见过类似这个库, 是带ripple效果的 不记得具体地址了,和这个类似 https://github.com/Ashok-Varma/BottomNav...

10530
来自专栏Android先生

Activity、View、Window的理解一篇文章就够了

要了解这三者之间的关系,我们带着问题通过分析源码一步一步来揭开它们的神秘面纱! 文章有点长,首先要理解Activity、View、Window,我提出了一些问题...

11010

扫码关注云+社区

领取腾讯云代金券