MVP是在开发中常用的框架,要了解其原理,先要从了解MVC开始,这里就对MVP框架做一个简单的介绍

MVC

MVC为Model,View与Controllor的缩写

Model:业务逻辑和实体模型

View:对应于布局文件,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller

Controllor:对应于Activity

MVP

MVC为Model,View与Presenter的缩写

Model:业务逻辑和实体模型

View:对应于Activity,负责View的绘制以及与用户交互

Presenter:负责完成View于Model间的交互

MVP对比MVC

  • 减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低

  • Activity 代码变得更加简洁:使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多

  • 方便进行单元测试:一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧

  • 避免 Activity 的内存泄露:

    • 发生OOM异常的原因:现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak));Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收;Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM
    • MVC产生内存泄漏异常分析:采用传统的MVC模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM
    • MVC模式如何比面内存泄漏:只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak

MVP角色

View::负责绘制UI元素、与用户进行交互(在Android中体现为Activity)

Activity interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试

Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)

Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑

开源MVP框架

理解实例

这里使用一个ListView的设计来说明MVP在实际项目开发过程中运用

首先准备一个java bean类,用来作为ListView的显示部分

public class Fruit {
private int icon;
private String name;
private String describe; public Fruit(int icon, String name, String describe) {
this.icon = icon;
this.name = name;
this.describe = describe;
} public int getIcon() {
return icon;
} public void setIcon(int icon) {
this.icon = icon;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getDescribe() {
return describe;
} public void setDescribe(String describe) {
this.describe = describe;
}
}

另外还需要一个数据类

public class FruitListAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Fruit> data; public FruitListAdapter(Context context, List<Fruit> data) {
this.inflater = LayoutInflater.from(context);
this.data = data;
} @Override
public int getCount() {
return data.size();
} @Override
public Object getItem(int position) {
return data.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item, null);
holder = new ViewHolder();
holder.iv_icon = convertView.findViewById(R.id.iv_icon);
holder.tv_name = convertView.findViewById(R.id.tv_name);
holder.tv_describe = convertView.findViewById(R.id.tv_describe);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.iv_icon.setImageResource(data.get(position).getIcon());
holder.tv_name.setText(data.get(position).getName());
holder.tv_describe.setText(data.get(position).getDescribe());
return convertView;
} private class ViewHolder {
ImageView iv_icon;
TextView tv_name;
TextView tv_describe;
}
}

model层

public interface IFruitModel {
void loadFruit(FruitOnLoadListener fruitOnLoadListener); //监听数据返回
interface FruitOnLoadListener {
void onComplete(List<Fruit> fruits);
}
}
public class FruitModel implements IFruitModel {
@Override
public void loadFruit(FruitOnLoadListener fruitOnLoadListener) {
List<Fruit> data = new ArrayList<>();
data.add(new Fruit(R.drawable.p01, "石榴", "性味甘、酸涩、温,具有杀虫、收敛、涩肠、止痢等功效。石榴果实营养丰富,维生素C含量比苹果、梨要高出一二倍"));
data.add(new Fruit(R.drawable.p02, "荔枝", "荔枝味甘、酸、性温,入心、脾、肝经;可止呃逆,止腹泻,是顽固性呃逆及五更泻者的食疗佳品,同时有补脑健身,开胃益脾,有促进食欲之功效。因性热,多食易上火。荔枝木材坚实,纹理雅致,耐腐,历来为上等名材"));
data.add(new Fruit(R.drawable.p03, "猕猴桃", "猕猴桃的质地柔软,口感酸甜。味道被描述为草莓、香蕉、菠萝三者的混合。猕猴桃除含有猕猴桃碱、蛋白水解酶、单宁果胶和糖类等有机物,以及钙、钾、硒、锌、锗等微量元素和人体所需17种氨基酸外,还含有丰富的维生素C、葡萄酸、果糖、柠檬酸、苹果酸、脂肪"));
data.add(new Fruit(R.drawable.p04, "香瓜", "各种香瓜均含有苹果酸、葡萄糖、氨基酸、甜菜茄、维生素C等丰富营养。全国各地广泛栽培,世界温带至热带地区也广泛栽培"));
data.add(new Fruit(R.drawable.p05, "橘子", "橘子中的维生素A还能够增强人体在黑暗环境中的视力和治疗夜盲症。橘子不宜食用过量,吃太多会患有胡萝卜素血症,皮肤呈深黄色,如同黄疸一般"));
data.add(new Fruit(R.drawable.p06, "柠檬", "柠檬因其味极酸,肝虚孕妇最喜食,故称益母果或益母子。柠檬中含有丰富的柠檬酸,因此被誉为“柠檬酸仓库”。它的果实汁多肉脆,有浓郁的芳香气。因为味道特酸,故只能作为上等调味料,用来调制饮料菜肴、化妆品和药品"));
data.add(new Fruit(R.drawable.p07, "西瓜", "西瓜为夏季之水果,果肉味甜,能降温去暑;种子含油,可作消遣食品;果皮药用,有清热、利尿、降血压之效"));
data.add(new Fruit(R.drawable.p08, "苹果", "苹果是一种低热量食物,每100克只产生60千卡热量。苹果中营养成分可溶性大,易被人体吸收,故有“活水”之称。其有利于溶解硫元素,使皮肤润滑柔嫩"));
data.add(new Fruit(R.drawable.p09, "葡萄", "葡萄为著名水果,生食或制葡萄干,并酿酒,酿酒后的酒脚可提酒食酸,根和藤药用能止呕、安胎"));
data.add(new Fruit(R.drawable.p10, "火龙果", "火龙果属凉性,且果肉的葡萄糖不甜,但其糖分却比一般水果的要高一些"));
data.add(new Fruit(R.drawable.p11, "草莓", "原产南美,中国各地及欧洲等地广为栽培。草莓营养价值高,含有多种营养物质 ,且有保健功效"));
data.add(new Fruit(R.drawable.p12, "柿子", "柿叶中含有多种活性成分,如维生素 C、 多种黄酮甙类、 二萜类、 胆碱、 β- 胡萝卜素等"));
data.add(new Fruit(R.drawable.p13, "香蕉", "香蕉是淀粉质丰富的有益水果。味甘性寒,可清热润肠,促进肠胃蠕动,但脾虚泄泻者却不宜。根据“热者寒之”的原理,最适合燥热人士享用。痔疮出血者、因燥热而致胎动不安者,都可生吃蕉肉"));
data.add(new Fruit(R.drawable.p14, "菠萝", "菠萝性平,味甘、微酸、微涩、性微寒,具有清暑解渴、消食止泻、补脾胃、固元气、益气血、消食、祛湿、养颜瘦身等功效,为夏令医食兼优的时令佳果,不过一次也不宜吃太多"));
data.add(new Fruit(R.drawable.p15, "梨子", "梨含有大量蛋白质、脂肪、钙、磷、铁和葡萄糖、果糖、苹果酸、胡萝卜素及多种维生素。梨还是治疗疾病的良药,民间常用冰糖蒸梨治疗喘咳,“梨膏糖”更是闻名中外。梨还有降血压、清热镇凉的作用,所以高血压及心脏病患者食梨大有益处"));
fruitOnLoadListener.onComplete(data);
}
}

View层

public interface IFruitView {
//UI业务逻辑,加载进度条
void showLoading();
//回调给Present
void showFruit(List<Fruit> fruits);
}

Present层

public class FruitPresent {
//持有视图层引用
private IFruitView fruitView;
//持有模型层引用
private IFruitModel fruitModel = new FruitModel(); public FruitPresent(IFruitView fruitView) {
this.fruitView = fruitView;
} public void fectch() {
fruitView.showLoading();
if (fruitModel != null) {
//回调监听
fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
@Override
public void onComplete(List<Fruit> fruits) {
fruitView.showFruit(fruits);
}
});
}
}
}

MainActivity调用,这里的MainActivity其实就是View层的实现

//View层
public class MainActivity extends AppCompatActivity implements IFruitView { private ListView listView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
//实例化Present并得到数据
new FruitPresent(this).fectch();
} @Override
public void showLoading() { } @Override
public void showFruit(List<Fruit> fruits) {
listView.setAdapter(new FruitListAdapter(this,fruits));
}
}

这里还应该对其优化,防止内存泄漏

增加BasePresent类,使其添加绑定和解绑操作

public abstract class BasePresent<T> {
//持有UI接口的弱引用
protected WeakReference<T> viewRef; //获取数据方法
public abstract void fectch(); //弱引用绑定
public void attachView(T view) {
viewRef = new WeakReference<T>(view);
} //解绑
public void detachView() {
if (viewRef != null) {
viewRef.clear();
viewRef = null;
}
}
}

让其子类继承

public class FruitPresent<T> extends BasePresent<IFruitView> {
//持有视图层引用
private IFruitView fruitView;
//持有模型层引用
private IFruitModel fruitModel = new FruitModel(); public FruitPresent(IFruitView fruitView) {
this.fruitView = fruitView;
} @Override
public void fectch() {
fruitView.showLoading();
if (fruitModel != null) {
//回调监听
fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
@Override
public void onComplete(List<Fruit> fruits) {
fruitView.showFruit(fruits);
}
});
}
}
}

添加BaseActivity,完成绑定和解绑操作

public abstract class BaseActivity<V, T extends BasePresent<V>> extends Activity {
protected T present; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
present = createPresent();
present.attachView((V) this);
} @Override
protected void onDestroy() {
super.onDestroy();
present.detachView();
} //子类实现具体构建过程
protected abstract T createPresent();
}

让其子类继承完成

//View层
public class MainActivity extends BaseActivity<IFruitView, FruitPresent<IFruitView>> implements IFruitView { private ListView listView; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_v1);
listView = findViewById(R.id.listview);
present.fectch();
} @Override
protected FruitPresent<IFruitView> createPresent() {
return new FruitPresent<>(this);
} @Override
public void showLoading() {
Toast.makeText(this, "loading", Toast.LENGTH_SHORT).show();
} @Override
public void showFruit(List<Fruit> fruits) {
listView.setAdapter(new FruitListAdapter(this, fruits));
}
}

移动架构之MVP框架的更多相关文章

  1. MVVM架构~mvc,mvp,mvvm大话开篇

    返回目录 百度百科的定义: MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新 ...

  2. [转]MVVM架构~mvc,mvp,mvvm大话开篇

    MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一 ...

  3. 如何实现自己的Android MVP框架?

    相信熟悉android开发的童鞋对MVP框架应该都不陌生吧,网上很多关于android中实现MVP的文章,大家可以直接搜索学习.这些文章中,MVP的实现思路基本都是把Activity.Fragment ...

  4. 玩转OneNET物联网平台之MQTT服务⑤ —— OneNet智能灯+MVP框架

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  5. 如何结合整洁架构和MVP模式提升前端开发体验 - 整体架构篇

    本文不详细介绍什么是整洁架构以及 MVP 模式,自行查看文章结尾相关链接文章. 整洁架构粗略介绍 下图为整洁架构最原始的结构图: Entities/Models:实体层,官方说法就是封装了企业里最通用 ...

  6. 【腾讯Bugly干货分享】一步一步实现Android的MVP框架

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5799d7844bef22a823b3ad44 内容大纲: Android 开发 ...

  7. mvp框架

    本文在于巩固基础 mvp框架的概念: MVP是MVC模式的另一个变种,MVP即可以应用到WEB项目中, 也可以应用到Winform项目中,它的方便的测试机制为大型复杂的企业级应用带来了福音,MVP模式 ...

  8. 微服务架构的基础框架选择:Spring Cloud还是Dubbo?

    最近一段时间不论互联网还是传统行业,凡是涉及信息技术范畴的圈子几乎都在讨论微服务架构.近期也看到各大技术社区开始组织一些沙龙和论坛来分享Spring Cloud的相关实施经验,这对于最近正在整理Spr ...

  9. Android Studio开发快速创建MVP框架插件AndroidMVP

    转载:https://www.jianshu.com/p/60cd98bbc358 Android开发中,我们为了代码的解耦以及后期的维护方便,都会采用一些开发框架,常用的有MVC.MVP.MVVM. ...

随机推荐

  1. 十五.DNS子域授权、分离解析、缓存DNS服务器

    1.搭建基本DNS服务器 pc7: 1.1 安装软件包 ]# yum -y install bind-chroot bind bind         //域名服务包 bind-chroot  //提 ...

  2. 三十一.MySQL存储引擎 、 数据导入导出 管理表记录 匹配条件

    1.MySQL存储引擎的配置 查看服务支持的存储引擎 查看默认存储类型 更改表的存储引擎 设置数据库服务默认使用的存储引擎 1.1 查看存储引擎信息 mysql> SHOW ENGINES\G ...

  3. java上传1t文件

    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用.此控件PC全平台支持包括mac,linux系统的文件上传,文章末尾将附上控件下载与教程链接 ...

  4. WPS-系统缺失字体

    WPS-系统缺失字体 小书匠 linux  在Linux上新安装WPS后,第一次打开就出现以下问题: wps系统缺失字体 问题原因: Linux上缺少windows字体,把字体添加上去即可. 操作步骤 ...

  5. (7)打鸡儿教你Vue.js

    计算属性 computed <div id="app"> {{ message.split('').reverse().join('') }} </div> ...

  6. CodeForces 750A New Year and Hurry

    #include<bits/stdc++.h> using namespace std; int main() { int n, k, i, sum; while(~scanf(" ...

  7. 安装cartographer遇到Unrecognized syntax identifier "proto3". This parser only recognizes "proto2"问题

    https://stackoverflow.com/questions/38605734/mac-cannot-find-eigen3 https://blog.csdn.net/qq_4214518 ...

  8. 线程池(2)-Executors提供4个线程池

    1.为什么不使用Executors提供4个线程池创建线程池 阿里巴巴开放手册这样写: . [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, ...

  9. GC的判定的2种方式

    对象是否死亡的2中判定方法:引用计数和可达性分析(又称引用链) 1.引用计数 对象再被创建时,对象头里会存储引用计数器,对象被引用,计数器+1:引用失效,计数器 -1:GC时会回收计数器为0的对象.但 ...

  10. 20165223《网络对抗技术》Exp 8 Web基础

    目录 -- Web基础 实践说明 实践目标 基础问答 实践内容 Web前端:HTML Web前端:JavaScript Web后端:MySQL Web后端:PHP SQL注入,XSS攻击测试 实验遇到 ...