写在前面:昨天晚上,公司请来专家讲解了下MVP,并要求今后各自负责的模块都要慢慢的转到MVP模式上来。以前由于能力有限,没有认真关注过设计模式、框架什么的,昨晚突然兴趣大发,故这两天空闲时间一直在学习MVP框架,公司不能上外网,不让带手机 ,只能吃饭坐班车时看看去公众号里搜点相关文章。想在此做个记录,希望原创者不要介意,再次感谢原创者


  出自公众号文章:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236921&idx=1&sn=4b2826b600a26b1cd3349ac91593b361&scene=1&srcid=0910LAe1TGDp0wpwPVoDx2Yo#wechat_redirect

  即博客地址:http://blog.csdn.net/study_zhxu/article/details/52152895


 
 

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前言

随着MVP概念的兴起和发展,MVP使用越来越广泛,当然MVP的优势也越来越被认同,在合作开发功能模块细分中MVP有着得天独厚的优势。本篇文章就来简单的说说如何使用MVP。

什么是MVP

MVP是MVC的变种,其实是一种升级。要说MVP就要说说MVC,在MVC中Activity其实是View层级,但是通常在使用中Activity即使View也是Controller,并没有将View层和Controller层进行分离, 
耦合度大大提高,非常不利于项目的管理。这时候MVP就应运而生了。

1、MVP分为三层

Model View Presenter

2、MVP模式的核心思想

MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。 
在MVP模式中Activity的功能就是响应生命周期和显示界面,具体其他的工作都丢到了Presenter层中进行完成,Presenter其实是Model层和View层的桥梁。 
具体的MVP的好处还有很多,就不详细介绍了,下面看看如何使用MVP。 
 
上面一张简单的MVP模式的UML图,从图中可以看出,使用MVP,至少需要经历以下步骤: 
1、创建 IPresenter 接口,把所有业务逻辑的接口都放在这里,并创建它的实现 PresenterCompl(在这里可以方便地查看业务功能,由于接口可以有多种实现所以也方便写单元测试)。 
2、创建 IView 接口,把所有视图逻辑的接口都放在这里,其实现类是当前的Activity/Fragment。 
3、由UML图可以看出,Activity里包含了一个IPresenter,而PresenterCompl里又包含了一个IView并且依赖了Model。Activity里只保留对IPresenter的调用,其它工作全部留到PresenterCompl中实现。 
4、Model并不是必须有的,但是一定会有View和Presenter。 
通过上面的介绍,MVP的主要特点就是把Activity里的许多逻辑都抽离到View和Presenter接口中去,并由具体的实现类来完成。这种写法多了许多IView和IPresenter的接口, 
在某种程度上加大了开发的工作量,刚开始使用MVP的小伙伴可能会觉得这种写法比较别扭,而且难以记住。其实一开始想太多也没有什么卵用,只要在具体项目中多写几次, 
就能熟悉MVP模式的写法,理解意图,以及享受其带来的好处。

MVP的使用

理论说了这么多其实没有什么卵用,下面就来在代码中看看如何使用吧。 
先来看看项目中代码结构,结构图如下 
 
通过代码结构图可以看到看出MVP结构层级分明(在项目中使用MVP最好通过模块进行分层这样便于管理且结构清晰),

先来看看View层代码 
ILoginView接口代码如下

 public interface ILoginView {
public void onClearText();
public void onLoginResult(Boolean result, int code);
}

可以看出ILoginView只是一个接口,简单的定义了两个方法。

看看ILoginView的实现类也就是我们的LoginActivity 
LoginActivity代码如下

 public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{

     private Button mLogin ;
private Button mClear ;
private EditText mName ;
private EditText mPassWord ; ILoginPresenter loginPresenter ; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mLogin = (Button) findViewById(R.id.btn_login);
mClear = (Button) findViewById(R.id.btn_clear);
mName = (EditText) findViewById(R.id.et_name);
mPassWord = (EditText) findViewById(R.id.et_password); mLogin.setOnClickListener(this);
mClear.setOnClickListener(this); loginPresenter = new LoginPresenterCompl(this) ; } @Override
public void onClearText() {
mName.setText("");
mPassWord.setText("");
Toast.makeText(this,"clear",Toast.LENGTH_SHORT).show();
} @Override
public void onLoginResult(Boolean result, int code) {
mLogin.setEnabled(true);
mClear.setEnabled(true); if(result){
Toast.makeText(this,"登陆成功: "+code,Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this,"登陆失败:"+code,Toast.LENGTH_SHORT).show();
}
} @Override
public void onClick(View v) {
int id = v.getId() ;
String name = mName.getText().toString() ;
String password = mPassWord.getText().toString() ; switch (id){
case R.id.btn_login :
loginPresenter.doLogin(name,password);
break ;
case R.id.btn_clear :
loginPresenter.clear();
break;
}
}
}

布局文件如下

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".view.LoginActivity"> <EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:hint="name"
android:ems="10"
android:id="@+id/et_name"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginTop="94dp" /> <EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="password"
android:ems="10"
android:id="@+id/et_password"
android:layout_below="@+id/et_name"
android:layout_alignParentStart="true" /> <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LOGIN"
android:id="@+id/btn_login"
android:layout_centerVertical="true"
android:layout_alignParentStart="true" /> <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLEAR"
android:id="@+id/btn_clear"
android:layout_alignBottom="@+id/btn_login"
android:layout_alignParentEnd="true" />
</RelativeLayout>

在LoginActivity中我们可以看到,LoginActivity实现了ILoginView接口,实现了未实现的方法,在代码中可以看出LoginActivity并没有做一些逻辑处理工作,数据处理的工作都是调用ILoginPresenter 
完成的。下面就来看看ILoginPresenter

 public interface ILoginPresenter {
public void clear() ;
public void doLogin(String name, String password) ;
}

也是简单的接口定义两个未实现的方法。 
看看其实现类LoginPresenterCompl

 public class LoginPresenterCompl implements ILoginPresenter {

     private ILoginView loginView ;
private User user ; @Inject
public LoginPresenterCompl(ILoginView view){
loginView = view ;
user = new User("张三","123456") ;
} @Override
public void clear() {
loginView.onClearText();
} @Override
public void doLogin(String name, String password) {
boolean result = false ;
int code = 0 ;
if(name.equals(user.getName()) && password.equals(user.getPassword())){
result = true ;
code = 1 ;
}else{
result = false ;
code = 0 ;
} loginView.onLoginResult(result,code);
}
}

该实现类也比较简单,定义了用户名是张三,密码是123456的一个登陆用户,然后进行登陆和清除的操作。 
OK这就完成了最简单的MVP模式了。

看看model层的代码 
User的代码如下

 public class User {

     private String name ;
private String password ; public User(String name,String password){
this.name = name ;
this.password = password ;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
}
}

看完上述MVP的代码后可能有的同学会说一个简单的登陆就是简单的验证用户名和密码为什么要设计的这么复杂,可能有人会觉得还没有以前的写法简单呢。但是这只是一个简单的实现MVP的例子。 
当项目越来越大,代码越来越多的时候就能够真正的体现出MVP模式的优势了。

知道了什么是MVP也知道了如何在项目中使用MVP,那MVP的优势到底有哪些呢?

MVP的优势

1、使Activity中的代码更加简洁 
在传统的项目中Activity兼顾着Controller和View,这使得其代码分分钟上千行(本人深受其害),这使得代码难以理解难以维护,看到这样的Activity就想吐。 
使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了, 
而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。

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

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

总结

以上就是MVP的简单实现,可能示例代码太简单无法体现MVP的优势,但是理解了MVP的思路在项目中使用MVP就才能够真正体验到MVP带来的好处优势。


01:59:11

设计模式笔记之一:MVP架构模式入门(转)的更多相关文章

  1. 用户登录(Material Design + Data-Binding + MVP架构模式)实现

    转载请注明出处: http://www.cnblogs.com/cnwutianhao/p/6772759.html MVP架构模式 大家都不陌生,Google 也给出过相应的参考 Sample, 但 ...

  2. iOS - MVP 架构模式

    1.MVP 从字面意思来理解,MVP 即 Modal View Presenter(模型 视图 协调器),MVP 实现了 Cocoa 的 MVC 的愿景.MVP 的协调器 Presenter 并没有对 ...

  3. <<.NET B/S 架构实践>> 几种概念区别 - 算法、设计模式、企业应用架构模式、架构模式

    算法:相信大家对算法肯定不陌生(但其实绝大多数开发人员对这个非常陌生且抗拒),因为从学校没毕业开始就已经被算法折磨了,哈哈 设计模式:爱学习的开发人员对这个也不会陌生,是些到了一定工作阶段必须学的思想 ...

  4. MVP架构模式

    概念解释 MVP是Model(数据) View(界面) Presenter(表现层)的缩写,它是MVC架构的变种,强调Model和View的最大化解耦和单一职责原则 Model:负责数据的来源和封装, ...

  5. 设计模式笔记:简单工厂模式(Simple Factory)

    1. 简单工厂模式简介 1.1 定义 简单工厂模式:定义一个Factory类,可以根据参数的不同返回不同类的实例,被创建的实例通常有共同的父类. 简单工厂模式:只需要一个Factory类. 简单工厂模 ...

  6. Android MVC,MVP,MVVM模式入门——重构登陆注册功能

    一  MVC模式: M:model,业务逻辑 V:view,对应布局文件 C:Controllor,对应Activity 项目框架: 代码部分: layout文件(适用于MVC和MVP两个Demo): ...

  7. Head First 设计模式笔记(模版方法模式)

    1.定义: 在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中.模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. 2.类图:  3.说明: 模版方法可以理解为一个方法里面包 ...

  8. head first 设计模式笔记8-模板方法模式

    模板设计模式:就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现. 优点:使用模板方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求. 缺点:如果算法骨架有修改的 ...

  9. head first 设计模式笔记2-观察者模式:气象观测站

    设计原则:为了交互对象之间的松耦合设计而努力. 1.设计模式的一些理解 1)知道OO基础,并不足以让你设计出良好的OO系统 2)良好的OO设计必须具备可复用.可扩充.可维护三个特性 3)模式可以让我们 ...

随机推荐

  1. CodeIgniter 如何去掉 Index.php

    步骤; 1.打开你的WEB服务器的httpd.conf文件. 2.找到LoadModule rewrite_module modules/mod_rewrite.so这行,把该行前的#去掉.保存该文件 ...

  2. offset获取位置

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  3. 2016.10.08--Intel Code Challenge Final Round--D. Dense Subsequence

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  4. View与ViewGroup有什么区别?

    百度知道:http://zhidao.baidu.com/link?url=B5MFOzDlww8soYqr5CL5FldH4sXD6eumS1XTRn8XEh8gu4mKjQdPkJSLIBt7u_ ...

  5. jquery 延迟执行实例介绍

    代码如下: $(function(){ var $inputs = $('input[type=button]') .delay(500) .queue(function(){$(this).hide ...

  6. Ubuntu Linux 环境变量

    2011年09月17日 Ubuntu 下设置adb环境变量 分类: 同Windows一样,Ubuntu Linux系统包含两类环境变量:系统环境变量和用户环境变量.系统环境变量对所有系统用户都有效,用 ...

  7. nat和打洞

    http://michankong.blog.51cto.com/1464983/761270 可能有点乱,下面以故事的形式叙述一下这个情景. 人物:A(男) NAT_A(A家接线员) B(女) NA ...

  8. 认识和选用常用的几种 GPRS 模块(转)

    源:http://blog.sina.com.cn/s/blog_4d80055a0100e8kr.html 我在这里把常见的GPRS模块分成3种: (1)GPRS DTU(GPRS数传单元,常称GP ...

  9. 【转】Grub Rescue修复方法

    症状: 开机显示: GRUB loading error:unknow filesystem grub rescue> 原因:已经发现下面几种操作会导致这种问题:1,想删除debian,于是直接 ...

  10. angularJs中ngModel的坑

    angular中指令被用的最多的除了ng-repeat之外就非他莫属了吧,由于各种业务的需求,我们经常需要去写一些继承ngModel的指令,来满足各种奇葩需求,最多的莫过于表单的验证啦,另外你在封装一 ...