前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >安卓MVP框架的简介与搭建

安卓MVP框架的简介与搭建

作者头像
緣來
发布2020-01-02 15:03:15
1.6K0
发布2020-01-02 15:03:15
举报
文章被收录于专栏:緣來來來

一、基本概念

MVP是Model-View-Presenter的简称,即模型-视图-表现层的缩写。MVP是由MVP模式进化而来的,MVP改进了MVC中的控制器过于臃肿的问题。 与MVC一样,MVP将应用程序的数据处理、数据显示和逻辑控制分开,用一种业务逻辑、数据显示和界面相分离的方法组织代码。

二、MVP与MVC的比较(以Android开发为例)

MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。

MVC

MVP

Model

业务逻辑和实体模型

Model

业务逻辑和实体模型

View

对性欲布局文件

View

对应于Activity,负责view的绘制和用户交互

Controller

对应于Activity等

Presenter

负责完成view与model的交互,处理程序逻辑

MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。

MVP与MVC相比,MVP减少了Activity的职责,简化了Activity的代码,将复杂的逻辑代码提取到了Presenter中进行处理。Presenter的出现,将Activity视为View层,Presenter负责完成View层与Model层的交互。与之对应的好处就是:程序耦合度更低,更加方便地进行测试,程序可扩展性大大提高。

MVP从MVC演化而来,它们的基本思想有相通的地方。Controller与Presenter负责逻辑的处理,Model提供数据,View负责显示数据。MVP作为一个新的模式,与MVC有一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部;而在MVC中View会直接从Model读取数据。

MVP解决了MVC问题: 在MVP中,Presenter完全把View与Model进行分离,主要的程序逻辑在Presenter实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View层的时候可以保持Presenter不变。不仅如此,我们还可以编写测试用的View,模拟用户的操作,从而实现对Presenter的测试——而不需要使用自动化的测试工具。 MVP中的View层是很薄的一层,View只应该有简单的set/get方法、用户输入和界面显示的内容,除此之外不应该有更多的内容,绝不允许直接访问Model——这就是MVP与MVC的很大不同之处。

三、MVP的工作原理和结构

  • 1、模型(Model) 模型表示业务逻辑和实体模型,提供数据给Presenter。
  • 2、视图(View) 视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接受用户的输入数据,但它不进行任何实际的业务处理。
  • 3、表现层(Presenter) 应用程序主要的程序逻辑在Presenter内实现,而且Presenter将Model和View完全分离,所有的交互都发生在Presenter内部,具体业务逻辑全部交由Presenter接口实现类中进行处理。

四、MVP的优点

  • 1、模型与视图完全分离,我们可以修改视图而不影响模型。
  • 2、可以更加高效地使用模型,因为所有的交互都发生在Presenter内部。
  • 3、我们可以将一个视图用于多个视图,而不需要改变Presenter内部的逻辑。这个特性非常有用,因为视图的变化总是比模型的变化要频繁。
  • 4、把程序逻辑放在Presenter中,我们就可以脱离用户接口来测试这些逻辑了。(单元测试)

五、MVP的缺点

由于对View的操作放在了Presenter中,所以View和Presenter的交互会过于频繁。如果Presenter过多地操作视图,往往会使得它与特定的 View联系过于紧密。一旦视图需要改变,那么Presenter也需要改变。

六、MVP模式框架的封装

首先看一下项目的基本结构图:

代码语言:javascript
复制
$ tree
.
├── model
│   ├── IPestModel.java
│   └── PestModelImpl.java
├── presenter
│   ├── BasePresenter.java
│   └── PestPresenter.java
└── view
    ├── BaseActivity.java
    └── IPestView.java

项目结构看起来像是这个样子的,MVP的分层还是很清晰的。我的习惯是先按模块分Package,在模块下面再去创建model、view、presenter的子Package,当然也可以用model、view、presenter作为顶级的Package,然后把所有的模块的model、view、presenter类都到这三个顶级Package中,就好像有人喜欢把项目里所有的Activity、Fragment、Adapter都放在一起一样。

Presenter层

BasePresenter.java

代码语言:javascript
复制
package com.ahau.againstpest.presenter;

import java.lang.ref.WeakReference;

/**
 * 注释:
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public class BasePresenter<T > {
    //1, view 层的引用
    protected WeakReference<T> mViewRef;


    //进行绑定
    public void attachView(T view) {
        mViewRef = new WeakReference<T>(view);
    }

    //进行解绑
    public void detachView() {
        mViewRef.clear();
    }
}

PestPresenter.java

代码语言:javascript
复制
package com.ahau.againstpest.presenter;

import com.ahau.againstpest.model.IPestModel;
import com.ahau.againstpest.model.PestModelImpl;
import com.ahau.againstpest.view.IPestView;

/**
 * 注释:表示层
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public class PestPresenter<T extends IPestView> extends BasePresenter<T> {

    //2, model层的引用
    IPestModel pestModel = new PestModelImpl();

    //3, 构造方法
    public PestPresenter() {
    }

    //4, 执行操作(UI)逻辑
    public void fetch() {

        if (mViewRef.get() != null) {
            if (pestModel != null) {
                //获取pest的名字
                pestModel.getPest(new IPestModel.PestGetListener() {
                    @Override
                    public void onComplete(String[] pest) {
                        if (mViewRef.get() != null) {
                            mViewRef.get().showPest(pest);
                        }
                    }
                });
                //设置当前时间
                pestModel.getDate(new IPestModel.DateListener() {
                    @Override
                    public void onComplete(String date) {
                        if (mViewRef.get() != null) {
                            mViewRef.get().setDate(date);
                        }
                    }
                });

                pestModel.getLatlng(new IPestModel.LatlngListener() {
                    @Override
                    public void onComplete(String[] latlng) {
                        if (mViewRef.get() != null) {
                            mViewRef.get().setLatlng(latlng);
                        }
                    }
                });
            }

        }


    }
}

View层

BaseActivity.java

代码语言:javascript
复制
package com.ahau.againstpest.view;

import android.app.Activity;
import android.os.Bundle;

import com.ahau.againstpest.presenter.BasePresenter;

/**
 * 注释:
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public abstract class BaseActivity<V,T extends BasePresenter<V>> extends Activity{

    //表示层的引用
    public T pestPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        pestPresenter = createPresenter();
        pestPresenter.attachView((V) this);
    }

    protected abstract T createPresenter();

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

IPestView.java

代码语言:javascript
复制
package com.ahau.againstpest.view;

/**
 * 注释:定义出所有UI逻辑
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public interface IPestView {
    //显示DropEditText的数据(使用会掉的方式返回数据)
    void showPest(String[] pest);
    //显示当前时间
    void setDate(String date);
    //获取当前的经纬度
    void setLatlng(String[] latlng);
}

Model层

IPestModel.java

代码语言:javascript
复制
package com.ahau.againstpest.model;

/**
 * 注释:用来加载数据
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public interface IPestModel {
    void getPest(PestGetListener pestGetListener);
    //设计一个内部回调接口
    interface PestGetListener {
        void onComplete(String[] pest);
    }

    void getDate(DateListener dateListener);
    interface DateListener {
        void onComplete(String date);
    }
    //获取当前经纬度数据
    void getLatlng(LatlngListener latlngListener);
    interface LatlngListener {
        void onComplete(String[] latlng);
    }

}
代码语言:javascript
复制
package com.ahau.againstpest.model;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.ahau.againstpest.HttpProcessor.HttpCallback;
import com.ahau.againstpest.HttpProcessor.HttpHelper;
import com.ahau.againstpest.base.MyApplication;
import com.ahau.againstpest.bean.PestBean;
import com.ahau.againstpest.utils.URLUtils;
import com.amap.api.maps2d.CoordinateConverter;
import com.amap.api.maps2d.model.LatLng;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 注释:
 *
 * @author fanchunchun
 * @date 2018/4/3
 */

public class PestModelImpl implements IPestModel {


    /**害虫的名字
     * @param pestGetListener
     */
    @Override
    public void getPest(final PestGetListener pestGetListener) {

        Map<String, Object> params = new HashMap<>();

        HttpHelper.obtain().get(URLUtils.PestUrl, params, new HttpCallback<PestBean>() {
            @Override
            public void onSuccess(PestBean pestBean) {

                ArrayList<PestBean.Pest> mPestNameList;
                String[] arr; //pest种类

                mPestNameList = pestBean.pest;
                Log.i("TAG", pestBean.toString());

                /**
                 * 设置虫种
                 */
                arr = new String[mPestNameList.size() + 1];
                arr[0] = "请选择:";
                for (int i = 0; i < mPestNameList.size(); i++) {
                    arr[i + 1] = mPestNameList.get(i).pestname;
                }
                Log.i("tag", arr[0]);
                pestGetListener.onComplete(arr);
            }

            @Override
            public void onFailure(String e) {
                Log.e("tag", "网络请求失败");
            }

        });

    }

    /**
     * 当前日期
     * @param dateListener
     */
    @Override
    public void getDate(DateListener dateListener) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("  yyyy/MM/dd  ");// yyyy/MM/dd  HH:mm:ss
        //获取当前时间
        Date date = new Date(System.currentTimeMillis());
        String sDate = simpleDateFormat.format(date);
        dateListener.onComplete(sDate);
    }

    /**
     * 经纬度数据
     * @param latlngListener
     */
    @Override
    public void getLatlng(LatlngListener latlngListener) {
        String[] latlng = new String[2];
        String jingdu, weidu, gaodejingdu, gaodeweidu; //经纬度

        LocationManager locationmanager = (LocationManager) MyApplication.getInstance().getSystemService(Context.LOCATION_SERVICE);

        if (ActivityCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationmanager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

        if (location != null) {
            double lat = location.getLatitude();// 经度
            double lng = location.getLongitude();// 纬度

            jingdu = "" + lat;
            weidu = "" + lng;

        } else {
            location = locationmanager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (location != null) {
                double lat = location.getLatitude();// 经度
                double lng = location.getLongitude();// 纬度
                jingdu = "" + lat;
                weidu = "" + lng;
            } else {
                jingdu = "";
                weidu = "";
            }
        }
        if (!jingdu.isEmpty() && !weidu.isEmpty()) {

            LatLng latLng = new LatLng(Double.valueOf(jingdu), Double.valueOf(weidu));
            CoordinateConverter converter = new CoordinateConverter();
            // CoordType.GPS 待转换坐标类型
            converter.from(CoordinateConverter.CoordType.GPS);
            // sourceLatLng待转换坐标点 LatLng类型
            converter.coord(latLng);
            // 执行转换操作
            LatLng desLatLng = converter.convert();
            gaodejingdu = "" + desLatLng.latitude;
            gaodeweidu = "" + desLatLng.longitude;
        }
        latlng[0] = jingdu;
        latlng[1] = weidu;

        latlngListener.onComplete(latlng);

    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基本概念
  • 二、MVP与MVC的比较(以Android开发为例)
  • 三、MVP的工作原理和结构
  • 四、MVP的优点
  • 五、MVP的缺点
  • 六、MVP模式框架的封装
    • Presenter层
      • View层
        • Model层
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档