淺談 MVP in Android
轉載請標明出處:
http://blog.csdn.net/lmj623565791/article/details/46596109;
本文出自:【張鴻洋的博客】
一、概述
對于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.bean;
/** * Created by zhy on 15/6/18. */
public class User {
private String username ;
private String password ;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
package com.zhy.blogcodes.mvp.biz;
/** * Created by zhy on 15/6/19. */
public interface IUserBiz {
public void login(String username, String password, OnLoginListener loginListener);
}
package com.zhy.blogcodes.mvp.biz;
import com.zhy.blogcodes.mvp.bean.User;
/** * Created by zhy on 15/6/19. */
public class UserBiz implements IUserBiz {
@Override
public void login(final String username, final String password, final OnLoginListener loginListener)
{
//模擬子線程耗時操作
new Thread()
{
@Override
public void run()
{
try
{
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);
} else
{
loginListener.loginFailed();
}
}
}.start();
}
}
package com.zhy.blogcodes.mvp.biz;
import com.zhy.blogcodes.mvp.bean.User;
/** * Created by zhy on 15/6/19. */
public interface OnLoginListener {
void loginSuccess(User user);
void loginFailed();
}
實體類不用說,至于業務類,我們抽取了一個接口,一個實現類這也很常見~~login方法,一般肯定是連接服務器的,是個耗時操作,所以我們開辟了子線程,Thread.sleep(2000)模擬了耗時,由于是耗時操作,所以我們通過一個回調接口來通知登錄的狀態。
其實這里還是比較好寫的,因為和傳統寫法沒區別。
(二) View
上面我們說過,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.view;
import com.zhy.blogcodes.mvp.bean.User;
/** * 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.mvp;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.zhy.blogcodes.R;
import com.zhy.blogcodes.mvp.bean.User;
import com.zhy.blogcodes.mvp.presenter.UserLoginPresenter;
import com.zhy.blogcodes.mvp.view.IUserLoginView;
public class UserLoginActivity extends ActionBarActivity implements IUserLoginView {
private EditText mEtUsername, mEtPassword;
private Button mBtnLogin, mBtnClear;
private ProgressBar mPbLoading;
private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
@Override
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()
{
@Override
public void onClick(View v)
{
mUserLoginPresenter.login();
}
});
mBtnClear.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mUserLoginPresenter.clear();
}
});
}
@Override
public String getUserName()
{
return mEtUsername.getText().toString();
}
@Override
public String getPassword()
{
return mEtPassword.getText().toString();
}
@Override
public void clearUserName()
{
mEtUsername.setText("");
}
@Override
public void clearPassword()
{
mEtPassword.setText("");
}
@Override
public void showLoading()
{
mPbLoading.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading()
{
mPbLoading.setVisibility(View.GONE);
}
@Override
public void toMainActivity(User user)
{
Toast.makeText(this, user.getUsername() +
" login success , to MainActivity", Toast.LENGTH_SHORT).show();
}
@Override
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.presenter;
import android.os.Handler;
import com.zhy.blogcodes.mvp.bean.User;
import com.zhy.blogcodes.mvp.biz.IUserBiz;
import com.zhy.blogcodes.mvp.biz.OnLoginListener;
import com.zhy.blogcodes.mvp.biz.UserBiz;
import com.zhy.blogcodes.mvp.view.IUserLoginView;
/** * Created by zhy on 15/6/19. */
public class UserLoginPresenter {
private IUserBiz userBiz;
private IUserLoginView userLoginView;
private Handler mHandler = new Handler();
public UserLoginPresenter(IUserLoginView userLoginView)
{
this.userLoginView = userLoginView;
this.userBiz = new UserBiz();
}
public void login()
{
userLoginView.showLoading();
userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener()
{
@Override
public void loginSuccess(final User user)
{
//需要在UI線程執行
mHandler.post(new Runnable()
{
@Override
public void run()
{
userLoginView.toMainActivity(user);
userLoginView.hideLoading();
}
});
}
@Override
public void loginFailed()
{
//需要在UI線程執行
mHandler.post(new Runnable()
{
@Override
public void run()
{
userLoginView.showFailedError();
userLoginView.hideLoading();
}
});
}
});
}
public void clear()
{
userLoginView.clearUserName();
userLoginView.clearPassword();
}
}
注意上述代碼,我們的presenter完成二者的交互,那么肯定需要二者的實現類。大致就是從View中獲取需要的參數,交給Model去執行業務方法,執行的過程中需要的反饋,以及結果,再讓View進行做對應的顯示。
ok,拿到一個例子經過上述的分解基本就能完成。以上純屬個人見解,歡迎討論和吐槽~ have a nice day ~。
微信公眾號:hongyangAndroid
(歡迎關注,第一時間推送博文信息)
參考資料
- https://github.com/zhengxiaopeng/Rocko-Android-Demos/tree/master/android-mvp
- https://github.com/antoniolg/androidmvp
- https://github.com/pedrovgs/EffectiveAndroidUI
- http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/
- http://magenic.com/Blog/Post/6/An-MVP-Pattern-for-Android
- http://antonioleiva.com/mvp-android/
- http://konmik.github.io/introduction-to-model-view-presenter-on-android.html
來自: http://blog.csdn.net//lmj623565791/article/details/46596109
