2018最火的微信群名怎么能加到富姐群?

安卓面试题(45)
什么是MVP呢?它又和我们常常听到的MVC有什么关系了以及区别呢?
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
MVP如何解决MVC的问题?
在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用! 不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。 我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。
在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View,而对Presenter没有任何的影响了。 如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。
在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model--这就是与MVC很大的不同之处。
MVP的优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
那么说了这么多又关于MVP的东西那么该如何写一个MVP的项目呢?请看下图:
先从项目目录结构上面来看
我们模拟一个需求:首先我们要进入一个&Splash界面,&Splash界面中,有一个
ProgressBar控件和TextView控件,我们判断它是否有网络连接,如果有的话就隐藏 &ProgressBar和跳转到MainActivity如果没有网络的话则显示ProgressBar和TextView,TextView则提示用户No internet。就这么简单的一个需求,我们看看如何用MVP模式做这个需求
首先我们看下M层是如何接口写的
style=&font-size:18&&package com.manning.androidhacks.hack020.presenter.
public interface IConnectionStatus {
boolean isOnline();
然后看看实现(我们主要是看MVP模式的使用,所以在此就不做网络连接的检查了,模拟一个状态)
&span style=&font-size:18&&package com.manning.androidhacks.hack020.presenter.model.
import com.manning.androidhacks.hack020.presenter.model.IConnectionS
public class ConnectionStatus implements IConnectionStatus {
public boolean isOnline() {
return true;
然后我们在来看看M的接口
&span style=&font-size:18&&package com.manning.androidhacks.hack020.
public interface ISplashView {
void showProgress();
void hideProgress();
void showNoInetErrorMsg();
void moveToMainView();
以及M的实现
&span style=&font-size:18&&package com.manning.androidhacks.hack020.view.
import android.app.A
import android.content.I
import android.os.B
import android.view.V
import android.widget.ProgressB
import android.widget.TextV
import com.manning.androidhacks.hack020.R;
import com.manning.androidhacks.hack020.presenter.SplashP
import com.manning.androidhacks.hack020.view.ISplashV
public class SplashActivity extends Activity implements ISplashView {
private TextView mTextV
private ProgressBar mProgressB
private SplashPresenter mPresenter = new SplashPresenter();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
mPresenter.setView(this);
mTextView = (TextView) findViewById(R.id.splash_text);
mProgressBar = (ProgressBar) findViewById(R.id.splash_progress_bar);
protected void onResume() {
super.onResume();
mPresenter.didFinishLoading();
public void showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
public void hideProgress() {
mProgressBar.setVisibility(View.INVISIBLE);
public void showNoInetErrorMsg() {
mTextView.setText(&No internet&);
public void moveToMainView() {
startActivity(new Intent(this, MainActivity.class));
最后我们来看看P层是如何控制他们的逻辑的:
&span style=&font-size:18&&package com.manning.androidhacks.hack020.view.
import android.app.A
import android.content.I
import android.os.B
import android.view.V
import android.widget.ProgressB
import android.widget.TextV
import com.manning.androidhacks.hack020.R;
import com.manning.androidhacks.hack020.presenter.SplashP
import com.manning.androidhacks.hack020.view.ISplashV
public class SplashActivity extends Activity implements ISplashView {
private TextView mTextV
private ProgressBar mProgressB
private SplashPresenter mPresenter = new SplashPresenter();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
mPresenter.setView(this);
mTextView = (TextView) findViewById(R.id.splash_text);
mProgressBar = (ProgressBar) findViewById(R.id.splash_progress_bar);
protected void onResume() {
super.onResume();
mPresenter.didFinishLoading();
public void showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
public void hideProgress() {
mProgressBar.setVisibility(View.INVISIBLE);
public void showNoInetErrorMsg() {
mTextView.setText(&No internet&);
public void moveToMainView() {
startActivity(new Intent(this, MainActivity.class));
好,我个人理解,就是把逻辑层抽出来成P层,要是遇到需求逻辑上的更改就可以只需要修改P层了或者遇到逻辑上的大概我们可以直接从写一个P也可以,现在我看过的大多数开发把所有的东西都写在了Activity里面这样一来遇到改频繁改需求的时候,Activity里面就会被写的乱糟糟,所以想到了这个MVP模式希望能帮助到大家&,大家是不是觉得MVP很不错呢?觉得不错的同学们就应用到你的实战开发中去吧~!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:35955次
排名:千里之外
原创:21篇
转载:187篇
(3)(1)(5)(37)(16)(22)(128)MVP在Android项目中的简单体现
通过简单案例来说明MVP的使用,retrofit2+rxjava+mvp项目地址:##前言###什么是MVP?MVP模式是一种架构模式,也是一种经典的界面模式。MVP中的M代表Model, V是View, P是Presenter。Model 一部分是处理业务逻辑,一部分是提供View显示的数据。View 代表的是一个接口,一个将UI界面提炼而抽象出来的接口。Presenter Model和View之间的桥梁###MVP在Android项目中的其中一种体现方式经过查阅网上一些MVP的文章之后,有部分案例在presenter中实现具体的逻辑或者把Model单纯的看作是具体的Bean,个人觉得是不太准确的,MVX(MVC、MVP和MVVM)中,M的职责都应该包含两部分业务逻辑和提供View显示的数据,而X的部分则是为了实现UI界面和业务逻辑解耦的桥梁,在Android项目中使用MVP架构模式,以下这两种架构方式是我比较能接受和认可的。按照模块分包|----包名
|&&&&|----base
|&&&&|&&&&&&&&BaseActivity&&&&&&&&Activity基类
|&&&&|&&&&&&&&BaseMVPActivity&&&&&&&&MVP&Activity基类
|&&&&|&&&&&&&&BaseModel&&&&&&&&&&&&Model基类
|&&&&|&&&&&&&&BaseFragment&&&&&&&&Fragment基类
|&&&&|&&&&&&&&IBaseDelegate&&&&&&&&简化Presenter在Activity的实现
|&&&&|&&&&&&&&IBasePresenter&&&&&&&&Presenter基类
|&&&&|&&&&&&&&IBaseView&&&&&&&&&&&&View基类
|&&&&|----模块名1
|&&&&|&&&&|----model&&&&&&&&&&&&&&&&业务逻辑和bean
|&&&&|&&&&|&&&&&&&&xxxModel
|&&&&|&&&&|&&&&&&&&xxxBean
|&&&&|&&&&|----presenter&&&&&&&&&&&&连接View和Model的桥梁
|&&&&|&&&&|&&&&&&&&IxxxPresenter
|&&&&|&&&&|&&&&&&&&xxxPresenter
|&&&&|&&&&|----ui&&&&&&&&&&&&&&&&&&&&UI界面相关的类
|&&&&|&&&&|&&&&&&&&xxxActivity
|&&&&|&&&&|&&&&&&&&xxxFragment
|&&&&|&&&&|----view&&&&&&&&&&&&&&&&UI界面提炼出来的接口
|&&&&|&&&&|&&&&&&&&xxxView
|&&&&|----模块名2按照功能分包|----包名
|&&&&|----activity&&&&&&&&&&&&&&&&具体Activity
|&&&&|&&&&&&&&xxxActivity
|&&&&|----adapter&&&&&&&&&&&&&&&&&&&&具体Adapter
|&&&&|&&&&&&&&xxxAdapter
|&&&&|----base
|&&&&|&&&&&&&&BaseActivity&&&&&&&&Activity基类
|&&&&|&&&&&&&&BaseMVPActivity&&&&&&&&MVP&Activity基类
|&&&&|&&&&&&&&BaseModel&&&&&&&&&&&&Model基类
|&&&&|&&&&&&&&BaseFragment&&&&&&&&Fragment基类
|&&&&|&&&&&&&&IBaseDelegate&&&&&&&&简化Presenter在Activity的实现
|&&&&|&&&&&&&&IBasePresenter&&&&&&&&Presenter基类
|&&&&|&&&&&&&&IBaseView&&&&&&&&&&&&View基类
|&&&&|----fragment&&&&&&&&&&&&&&&&具体Fragment
|&&&&|&&&&&&&&xxxFragment
|&&&&|----hodler&&&&&&&&&&&&&&&&&&&&具体Holder
|&&&&|&&&&&&&&xxxHodler
|&&&&|----model&&&&&&&&&&&&&&&&&&&&业务逻辑和bean
|&&&&|&&&&&&&&xxxModel
|&&&&|&&&&&&&&xxxBean
|&&&&|----presenter&&&&&&&&&&&&&&&&连接View和Model的桥梁
|&&&&|&&&&&&&&IxxxPresenter
|&&&&|&&&&&&&&xxxPresenter
|&&&&|----view&&&&&&&&&&&&&&&&&&&&UI界面提炼出来的接口
|&&&&|&&&&&&&&xxxView
|&&&&|----widget##前期准备这里使用提供的免费API来实现两个具体的功能历史上的今天和笑话大全,注册并实名为聚合数据的用户后,生成属于自己的用户key即可。##快速开始###step1 添加所需的依赖和权限新建一个项目,在根目录的build.gradle的dependencies节点中添加,用于注解&&&&classpath&'com.neenbedankt.gradle.plugins:android-apt:1.8'如图主程序app module中build.gradle的第二行添加,用于注解apply&plugin:&'com.neenbedankt.android-apt'dependencies节点中添加compile&'com.android.support:appcompat-v7:24.2.1'
compile&'com.android.support:design:24.2.1'
//布局注解
apt&'com.jakewharton:butterknife-compiler:8.0.1'
compile&'com.jakewharton:butterknife:8.0.1'
//响应式编程
compile&'io.reactivex:rxandroid:1.1.0'
compile&'io.reactivex:rxjava:1.1.0'
//联网类库
compile&'com.squareup.retrofit2:retrofit:2.1.0'
compile&'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile&'com.squareup.retrofit2:converter-scalars:2.1.0'
compile&'com.dou361.retrofit2:jjdxm-retrofit-converter-fastjson:1.0.0'
compile&'com.squareup.okhttp3:okhttp:3.3.0'
//自定义view
compile&'com.dou361.customui:jjdxm-customui:1.0.9'
//recyclerview基类
compile('com.dou361.recyclerview:jjdxm-recyclerview:1.0.2')&{
&&&&exclude&group:&'com.android.support',&module:&'design'
}如图清单文件AndroidManifest.xml中,添加权限&uses-permission&android:name=&android.permission.INTERNET&/&###step2先写好两个网络请求方法,Observable&searchHistory(String month, String day)和static Observable&loadJoke(int page)分别是查询历史今天方法和加载笑话列表网络接口请求服务类public&interface&IApiService&{
&&&&/**&查询历史的今天&*/
&&&&@GET(&/japi/toh&)
&&&&Observable&RepoHistory&&searchHistory(@QueryMap&Map&String,&String&&map);
&&&&/**&加载笑话列表&*/
&&&&@GET(&/joke/content/list.from&)
&&&&Observable&RepoJoke&&loadJoke(@QueryMap&Map&String,&String&&map);
}网络接口请求基类public&class&ApiBase&{
&&&&/**历史上的今天&/japi/toh?key=7ac7e02ff7f1f8f1ccdc2f9e5dddb6be&v=1
&&&&&*&.0&month=11&day=1*/
&&&&/**&笑话大全&/joke/content/list
&&&&&*&.from?key=d796a03545bddee0b56df199&page=2&pagesize=10&sort=asc&time=&*/
&&&&protected&static&IApiService&getService()&{
&&&&&&&&return&getService(null);
&&&&protected&static&IApiService&getService(String&ip)&{
&&&&&&&&return&getService(ip,&0,&0);
&&&&protected&static&IApiService&getService(String&ip,&long&readTime,&long&connectTime)&{
&&&&&&&&OkHttpClient&client&=&new&OkHttpClient.Builder()
&&&&&&&&&&&&&&&&.readTimeout(readTime&&=&0&?&30&:&readTime,&TimeUnit.SECONDS)
&&&&&&&&&&&&&&&&.connectTimeout(connectTime&&=&0&?&30&:&connectTime,&TimeUnit.SECONDS)
&&&&&&&&&&&&&&&&.build();
&&&&&&&&Retrofit&retrofit&=&new&Retrofit.Builder()
&&&&&&&&&&&&&&&&.baseUrl(ip&==&null&?&&&&:&ip)
&&&&&&&&&&&&&&&&.client(client)
&&&&&&&&&&&&&&&&.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
&&&&&&&&&&&&&&&&.addConverterFactory(ScalarsConverterFactory.create())
&&&&&&&&&&&&&&&&.addConverterFactory(FastJsonConverterFactory.create())
&&&&&&&&&&&&&&&&.build();
&&&&&&&&return&retrofit.create(IApiService.class);
}网络接口请求工具类public&class&ApiUtils&extends&ApiBase&{
&&&&public&static&Observable&RepoHistory&&searchHistory(String&month,&String&day)&{
&&&&&&&&/**key=7ac7e02ff7f1f8f1ccdc2f9e5dddb6be&v=1.0&month=11&day=1*/
&&&&&&&&Map&String,&String&&map&=&new&HashMap&&();
&&&&&&&&map.put(&key&,&&7ac7e02ff7f1f8f1ccdc2f9e5dddb6be&);
&&&&&&&&map.put(&v&,&&1.0&);
&&&&&&&&map.put(&month&,&month);
&&&&&&&&map.put(&day&,&day);
&&&&&&&&return&getService().searchHistory(map);
&&&&public&static&Observable&RepoJoke&&loadJoke(int&page)&{
&&&&&&&&/**key=d796a03545bddee0b56df199&page=2&pagesize=10&sort=asc&time=*/
&&&&&&&&Map&String,&String&&map&=&new&HashMap&&();
&&&&&&&&map.put(&key&,&&d796a03545bddee0b56df199&);
&&&&&&&&map.put(&sort&,&&asc&);
&&&&&&&&map.put(&time&,&&&);
&&&&&&&&map.put(&page&,&page&+&&&);
&&&&&&&&map.put(&pagesize&,&&10&);
&&&&&&&&return&getService().loadJoke(map);
}如图###step3开始结构MVP模式使用到的基类,如图UI界面抽象出来的接口public&interface&IBaseView&{
&&&&&*&显示加载
&&&&void&showLoading();
&&&&&*&完成加载
&&&&void&dismiss();
}业务逻辑实现的基类public&abstract&class&BaseModel&IP&&{
&&&&protected&IP&mIP
&&&&public&BaseModel(IP&iPresenter)&{
&&&&&&&&this.mIPresenter&=&iP
}连接Model和View的桥梁的基类public&interface&IBasePresenter&V&extends&IBaseView&&{
&&&&/**绑定接口*/
&&&&void&attachView(V&view);
&&&&/**释放接口*/
&&&&void&detachView();
}persenter和activity绑定public&interface&IBaseDelegate&V&extends&IBaseView,&P&extends&IBasePresenter&V&&&{
&&&&/**初始化presenter*/
&&&&@NonNull
&&&&P&createPresenter();
&&&&/**获取presenter*/
&&&&@NonNull
&&&&P&getPresenter();
}最后是Activity的基类public&abstract&class&BaseActivity&extends
&&&&&&&&AppCompatActivity&{
&&&&protected&void&startActivity(Class&?&&clz)&{
&&&&&&&&Intent&intent&=&new&Intent(this,&clz);
&&&&&&&&startActivity(intent);
}MVP的Activity基类public&abstract&class&BaseActivity&V&extends&IBaseView,&P&extends&IBasePresenter&V&&&extends
&&&&&&&&AppCompatActivity&implements&IBaseDelegate&V,&P&&{
&&&&protected&P&mP
&&&&@Override
&&&&protected&void&onCreate(Bundle&savedInstanceState)&{
&&&&&&&&super.onCreate(savedInstanceState);
&&&&&&&&mPresenter&=&createPresenter();
&&&&@NonNull
&&&&@Override
&&&&public&P&getPresenter()&{
&&&&&&&&return&mP
&&&&@Override
&&&&protected&void&onDestroy()&{
&&&&&&&&mPresenter.detachView();
&&&&&&&&super.onDestroy();
}###step4针对模块用MVP模式去架构大概有一下4个步骤步骤1:UI实现View方法,引用Presenter
步骤2:Presenter调用Model,走Model具体逻辑
步骤3:Model逻辑实现,回调Presenter方法
步骤4:Presenter回调View,即回到UI,回调View方法###step5具体模块功能的实现,历史的今天模块,先创建一个HistoryActivity继承BaseMVPActivity,新建IHistoryView并实现。####1.View的接口的抽取抽象出来三个功能和父类IBaseView的两个方法,分别是显示加载好的数据,显示空白数据提示,检测数据提示,显示加载中提示,隐藏加载中提示。public&interface&IHistoryView&extends&IBaseView&{
&&&&/**显示数据*/
&&&&void&showData(List&HistoryBean&&list);
&&&&/**无数据*/
&&&&void&showEmpty();
&&&&/**检测数据*/
&&&&void&showMessage(String&msg);
}####2.Model的连接对应的新建IHistoryPresenter,此接口作用是连接Modelpublic&interface&IHistoryPresenter&{
&&&&/**显示数据*/
&&&&void&showData(List&HistoryBean&&list);
&&&&/**提示无数据*/
&&&&void&showEmpty();
&&&&/**数据检测提示*/
&&&&void&showMessage(String&msg);
}####3.Model的实现具体的逻辑实现,这里只有一个方法就是查询历史今天public&class&HistoryModel&extends&BaseModel&IHistoryPresenter&&{
&&&&public&HistoryModel(IHistoryPresenter&iPresenter)&{
&&&&&&&&super(iPresenter);
&&&&public&void&searchHistory(String&month,&String&day)&{
&&&&&&&&if&(TextUtils.isEmpty(month))&{
&&&&&&&&&&&&mIPresenter.showMessage(&月份不能为空&);
&&&&&&&&&&&&
&&&&&&&&int&iMonth&=&Integer.valueOf(month).intValue();
&&&&&&&&if&(iMonth&&=&0&||&iMonth&&&12)&{
&&&&&&&&&&&&mIPresenter.showMessage(&只能输入1-12的月份&);
&&&&&&&&&&&&
&&&&&&&&if&(TextUtils.isEmpty(day))&{
&&&&&&&&&&&&mIPresenter.showMessage(&天不能为空&);
&&&&&&&&&&&&
&&&&&&&&int&iDay&=&Integer.valueOf(day).intValue();
&&&&&&&&if&(iDay&&=&0&||&iDay&&&31)&{
&&&&&&&&&&&&mIPresenter.showMessage(&只能输入1-31的天&);
&&&&&&&&&&&&
&&&&&&&&ApiUtils.searchHistory(month,&day)
&&&&&&&&&&&&&&&&.observeOn(AndroidSchedulers.mainThread())
&&&&&&&&&&&&&&&&.subscribeOn(Schedulers.newThread())
&&&&&&&&&&&&&&&&.subscribe(new&Action1&RepoHistory&()&{
&&&&&&&&&&&&&&&&&&&&@Override
&&&&&&&&&&&&&&&&&&&&public&void&call(RepoHistory&repoHistory)&{
&&&&&&&&&&&&&&&&&&&&&&&&if&(repoHistory&==&null&||&repoHistory.getResult()&==&null
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&||&repoHistory.getResult().size()&&=&0)&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&mIPresenter.showEmpty();
&&&&&&&&&&&&&&&&&&&&&&&&}&else&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&mIPresenter.showData(repoHistory.getResult());
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&});
}####4.Presenter桥梁的实现public&class&HistoryPresenter&implements&IBasePresenter&IHistoryView&,&IHistoryPresenter&{
&&&&private&IHistoryView&mV
&&&&private&HistoryModel&mM
&&&&public&HistoryPresenter(IHistoryView&view)&{
&&&&&&&&attachView(view);
&&&&&&&&mModel&=&new&HistoryModel(this);
&&&&@Override
&&&&public&void&attachView(IHistoryView&view)&{
&&&&&&&&this.mView&=&
&&&&@Override
&&&&public&void&detachView()&{
&&&&&&&&this.mView&=&
&&&&@Override
&&&&public&void&showData(List&HistoryBean&&list)&{
&&&&&&&&mView.dismiss();
&&&&&&&&mView.showData(list);
&&&&@Override
&&&&public&void&showEmpty()&{
&&&&&&&&mView.dismiss();
&&&&&&&&mView.showEmpty();
&&&&@Override
&&&&public&void&showMessage(String&msg)&{
&&&&&&&&mView.showMessage(msg);
&&&&public&void&searchHistory(String&month,&String&day)&{
&&&&&&&&mView.showLoading();
&&&&&&&&mModel.searchHistory(month,&day);
}####5.最后在HistoryActivity里面去建立连接最后创建的类架构图如下:编译运行效果图如下同理笑话大全也一样的创建对应的文件,最后运行如下项目地址:作者:jjdxmashl
评论内容:178878人阅读
【android 进阶之路】(74)
转载请标明出处:
对于MVP(Model View Presenter),大多数人都能说出一二:“MVC的演化版本”,“让Model和View完全解耦”等等。本篇博文仅是为了做下记录,提出一些自己的看法,和帮助大家如何针对一个Activity页面去编写针对MVP风格的代码。
对于MVP,我的内心有一个问题:
为何这个模式出来后,就能被广大的Android的程序员接受呢?
问了些程序员,他们对于MVP的普遍的认识是:“代码很清晰,不过增加了很多类”。我在第一次看到MVP的时候,看了一个demo,看完以后觉得非常nice(但是回过头来,自己想个例子写,就头疼写不出来,当然这在后文会说)。nice的原因还是因为,这个模式的确让代码的清晰度有了很大的提升。
那么,提升一般都是对比出来的,回顾下,没有应用MVP的代码结构。很多人说明显是MVC么:
View:对应于布局文件
Model:业务逻辑和实体模型
Controllor:对应于Activity
看起来的确像那么回事,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller(当然了Data-Binder的出现,可能会让View更像View吧)。这可能也就是为何,在中有一句这样的话:
Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.
而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的:
View 对应于Activity,负责View的绘制以及与用户交互
Model 依然是业务逻辑和实体模型
Presenter 负责完成View于Model间的交互
ok,先简单了解下,文中会有例子到时候可以直观的感受下。
小总结下,也就是说,之所以让人觉得耳目一新,是因为这次的跳跃是从并不标准的MVC到MVP的一个转变,减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低,更方便的进行测试。借用两张图(出自:),代表上述的转变:
二、MVP 与 MVC 区别
ok,上面说了一堆理论,下面我们还是需要看一看MVC与MVP的一个区别,请看下图(来自:):
其实最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的(代码中会体现)。
还有一堆概念性的东西,以及优点就略了,有兴趣自行百度。下面还是通过一些简单的需求来展示如何编写MVP的demo。
三、Simple Login Demo
看到这样的效果,先看下完工后的项目结构:
ok,接下来开始一步一步的编写思路。
(一)Model
首先实体类User不用考虑这个肯定有,其次从效果图可以看到至少有一个业务方法login(),这两点没什么难度,我们首先完成:
package com.zhy.blogcodes.mvp.
* Created by zhy on 15/6/18.
public class User
public String getUsername()
public void setUsername(String username)
this.username =
public String getPassword()
public void setPassword(String password)
this.password =
package com.zhy.blogcodes.mvp.
* Created by zhy on 15/6/19.
public interface IUserBiz
public void login(String username, String password, OnLoginListener loginListener);
package com.zhy.blogcodes.mvp.
import com.zhy.blogcodes.mvp.bean.U
* Created by zhy on 15/6/19.
public class UserBiz implements IUserBiz
public void login(final String username, final String password, final OnLoginListener loginListener)
new Thread()
public void run()
Thread.sleep(2000);
} catch (InterruptedException e)
e.printStackTrace();
if ("zhy".equals(username) && "123".equals(password))
User user = new User();
user.setUsername(username);
user.setPassword(password);
loginListener.loginSuccess(user);
loginListener.loginFailed();
}.start();
package com.zhy.blogcodes.mvp.
import com.zhy.blogcodes.mvp.bean.U
* Created by zhy on 15/6/19.
public interface OnLoginListener
void loginSuccess(User user);
void loginFailed();
实体类不用说,至于业务类,我们抽取了一个接口,一个实现类这也很常见~~login方法,一般肯定是连接服务器的,是个耗时操作,所以我们开辟了子线程,Thread.sleep(2000)模拟了耗时,由于是耗时操作,所以我们通过一个回调接口来通知登录的状态。
其实这里还是比较好写的,因为和传统写法没区别。
上面我们说过,Presenter与View交互是通过接口。所以我们这里需要定义一个ILoginView,难点就在于应该有哪些方法,我们看一眼效果图:
可以看到我们有两个按钮,一个是login,一个是clear;
login说明了要有用户名、密码,那么对应两个方法:
String getUserName();
String getPassword();
再者login是个耗时操作,我们需要给用户一个友好的提示,一般就是操作ProgressBar,所以再两个:
void showLoading();
void hideLoading();
login当然存在登录成功与失败的处理,我们主要看成功我们是跳转Activity,而失败可能是去给个提醒:
void toMainActivity(User user);
void showFailedError();
ok,login这个方法我们分析完了~~还剩个clear那就简单了:
void clearUserName();
void clearPassword();
综上,接口完整为:
package com.zhy.blogcodes.mvp.
import com.zhy.blogcodes.mvp.bean.U
* Created by zhy on 15/6/19.
public interface IUserLoginView
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity(User user);
void showFailedError();
有了接口,实现就太好写了~~~
总结下,对于View的接口,去观察功能上的操作,然后考虑:
该操作需要什么?(getUserName, getPassword)
该操作的结果,对应的反馈?(toMainActivity, showFailedError)
该操作过程中对应的友好的交互?(showLoading, hideLoading)
下面贴一下我们的View的实现类,哈,其实就是Activity,文章开始就说过,MVP中的View其实就是Activity。
package com.zhy.blogcodes.
import android.os.B
import android.support.v7.app.ActionBarA
import android.view.V
import android.widget.B
import android.widget.EditT
import android.widget.ProgressB
import android.widget.T
import com.zhy.blogcodes.R;
import com.zhy.blogcodes.mvp.bean.U
import com.zhy.blogcodes.mvp.presenter.UserLoginP
import com.zhy.blogcodes.mvp.view.IUserLoginV
public class UserLoginActivity extends ActionBarActivity implements IUserLoginView
private EditText mEtUsername, mEtP
private Button mBtnLogin, mBtnC
private ProgressBar mPbL
private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_login);
initViews();
private void initViews()
mEtUsername = (EditText) findViewById(R.id.id_et_username);
mEtPassword = (EditText) findViewById(R.id.id_et_password);
mBtnClear = (Button) findViewById(R.id.id_btn_clear);
mBtnLogin = (Button) findViewById(R.id.id_btn_login);
mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
mBtnLogin.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
mUserLoginPresenter.login();
mBtnClear.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
mUserLoginPresenter.clear();
public String getUserName()
return mEtUsername.getText().toString();
public String getPassword()
return mEtPassword.getText().toString();
public void clearUserName()
mEtUsername.setText("");
public void clearPassword()
mEtPassword.setText("");
public void showLoading()
mPbLoading.setVisibility(View.VISIBLE);
public void hideLoading()
mPbLoading.setVisibility(View.GONE);
public void toMainActivity(User user)
Toast.makeText(this, user.getUsername() +
" login success , to MainActivity", Toast.LENGTH_SHORT).show();
public void showFailedError()
Toast.makeText(this,
"login failed", Toast.LENGTH_SHORT).show();
对于在Activity中实现我们上述定义的接口,是一件很容易的事,毕竟接口引导我们去完成。
最后看我们的Presenter。
(三)Presenter
Presenter是用作Model和View之间交互的桥梁,那么应该有什么方法呢?
其实也是主要看该功能有什么操作,比如本例,两个操作:login和clear。
package com.zhy.blogcodes.mvp.
import android.os.H
import com.zhy.blogcodes.mvp.bean.U
import com.zhy.blogcodes.mvp.biz.IUserB
import com.zhy.blogcodes.mvp.biz.OnLoginL
import com.zhy.blogcodes.mvp.biz.UserB
import com.zhy.blogcodes.mvp.view.IUserLoginV
* Created by zhy on 15/6/19.
public class UserLoginPresenter
private IUserBiz userB
private IUserLoginView userLoginV
private Handler mHandler = new Handler();
public UserLoginPresenter(IUserLoginView userLoginView)
this.userLoginView = userLoginV
this.userBiz = new UserBiz();
public void login()
userLoginView.showLoading();
userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener()
public void loginSuccess(final User user)
mHandler.post(new Runnable()
public void run()
userLoginView.toMainActivity(user);
userLoginView.hideLoading();
public void loginFailed()
mHandler.post(new Runnable()
public void run()
userLoginView.showFailedError();
userLoginView.hideLoading();
public void clear()
userLoginView.clearUserName();
userLoginView.clearPassword();
注意上述代码,我们的presenter完成二者的交互,那么肯定需要二者的实现类。大致就是从View中获取需要的参数,交给Model去执行业务方法,执行的过程中需要的反馈,以及结果,再让View进行做对应的显示。
ok,拿到一个例子经过上述的分解基本就能完成。以上纯属个人见解,欢迎讨论和吐槽~ have a nice day ~。
微信公众号:hongyangAndroid
(欢迎关注,第一时间推送博文信息)
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
积分:48336
积分:48336
排名:第58名
原创:201篇
评论:14476条
长期为您推荐优秀博文、开源项目、视频等,进入还有好玩的等着你,欢迎扫一扫。
请勿重复加群,Thx
文章:11篇
阅读:200335
文章:10篇
阅读:116314
文章:67篇
阅读:5778850}

我要回帖

更多关于 2018最火的微信群名 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信