介紹
MVC:
- View:對應於布局文件
- Model:業務邏輯和實體模型
- Controllor:對應於Activity
實際上關於該布局文件中的數據綁定的操作,事件處理的代碼都在Activity中,造成了Activity既像View又像Controller
程序員對於MVP的普遍的認識是: 代碼很清晰,不過增加了很多類
當將架構改為MVP以后,Presenter的出現,將Actvity視為View層,Presenter負責完成View層與Model層的交互。現在是這樣的:
- View 對應於Activity,負責View的繪制以及與用戶交互
- Model 依然是業務邏輯和實體模型
- Presenter 負責完成View和Model間的交互
從並不標准的MVC到MVP的一個轉變,減少了Activity的職責,簡化了Activity中的代碼,將復雜的邏輯代碼提取到了Presenter中進行處理。與之對應的好處就是,耦合度更低,更方便的進行測試。
MVC與MVP的一個區別示意圖:
其實最明顯的區別就是,MVC中是允許Model和View進行交互的,而MVP中很明顯,Model與View之間的交互由Presenter完成。還有一點就是Presenter與View之間的交互是通過接口的。
案例
Activity包(view包)
/*** 梳理此Activity所有需要的方法,將這些方法全部封裝到一個接口中* @author 白乾濤*/public interface IUserLoginView {public String getUserName();public String getPassword();public void clearUserName();public void clearPassword();public void toMainActivity(User user);public void showFailedError();public void showLoading();public void hideLoading();}
/*** 簡單來說,對Activity的所有操作(方法)都被剖離在了兩個地方,一個是在IUserLoginView中,這里面都是對Activity的一些最基本的操作* 另一個在Presenter中,比較復雜的【業務邏輯】都會放在這里(目的當然是減少Activity的代碼邏輯)* 一定要明白,之所以將Activity中的這些方法剖離在IUserLoginView中,是為了在Presenter中能通過操作IUserLoginView來操作Activity(擴展性)** @author 白乾濤*/public class UserLoginActivity extends Activity implements IUserLoginView, OnClickListener {private EditText id_et_username;private EditText id_et_password;private Button id_btn_login;private Button id_btn_clear;private UserLoginPresenter mUserLoginPrestener = new UserLoginPresenter(this);//這里是將IUserLoginView接口的實例(即Activity)傳給了Presenterprivate ProgressBar id_progressbar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_user_login);id_et_username = (EditText) findViewById(R.id.id_et_username);id_et_password = (EditText) findViewById(R.id.id_et_password);id_btn_login = (Button) findViewById(R.id.id_btn_login);id_btn_clear = (Button) findViewById(R.id.id_btn_clear);id_progressbar = (ProgressBar) findViewById(R.id.id_progressbar);id_btn_login.setOnClickListener(this);id_btn_clear.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.id_btn_login://這里面的兩個方法在Presenter中,當我們需要修改業務邏輯時,只需修改Prestener類中的相應方法即可!mUserLoginPrestener.login();break;case R.id.id_btn_clear:mUserLoginPrestener.clear();break;}}//******************************************************************************************@Overridepublic String getUserName() {return id_et_username.getText().toString();}@Overridepublic String getPassword() {return id_et_password.getText().toString();}@Overridepublic void clearUserName() {id_et_username.setText("");}@Overridepublic void clearPassword() {id_et_password.setText("");}@Overridepublic void toMainActivity(User user) {Toast.makeText(getApplicationContext(), user.getUsername() + "--登錄成功", Toast.LENGTH_SHORT).show();}@Overridepublic void showFailedError() {Toast.makeText(getApplicationContext(), "登錄失敗", Toast.LENGTH_SHORT).show();}@Overridepublic void showLoading() {id_progressbar.setVisibility(View.VISIBLE);}@Overridepublic void hideLoading() {id_progressbar.setVisibility(View.INVISIBLE);}}
presenter包
/*** 這就是傳說中的Prestener,這里面封裝的都是Activity中復雜的業務邏輯* @author 白乾濤*/public class UserLoginPresenter {private IUserBiz userBiz;private IUserLoginView userLoginView;private Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {};};//在構造Prestener時,需要傳遞一個IUserLoginView接口的實例(其實就是Activity),只有這樣,Prestener才能對此實例進行操作public UserLoginPresenter(IUserLoginView userLoginView) {this.userBiz = new UserBiz();this.userLoginView = userLoginView;}public void login() {userLoginView.showLoading();//為了更具擴展性及進一步解耦,具體的登錄邏輯又通過同樣的方式轉移到了IUserBiz接口上(統一放在biz包下)userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {@Overridepublic void loginSuccess(final User user) {//需要再UI線程執行mHandler.post(new Runnable() {@Overridepublic void run() {userLoginView.toMainActivity(user);userLoginView.hideLoading();}});}@Overridepublic void loginFailed() {//需要在UI線程執行mHandler.post(new Runnable() {@Overridepublic void run() {userLoginView.showFailedError();userLoginView.hideLoading();}});}});}public void clear() {userLoginView.clearPassword();userLoginView.clearUserName();}}
bean包
/*** 一個業務bean,所有需要被操作的基本元素(不包括View)都應該定義在這里* @author 白乾濤*/public class User {private String username;private String password;public String getUsername() {return username;}public String getPassword() {return password;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}}
biz(業務)包
/*** 復雜的業務邏輯在這里定義:如,登錄、忘記密碼、聯系客服* @author 白乾濤*/public interface IUserBiz {public void login(String username, String password, OnLoginListener onLoginListener);}
/*** 復雜業務邏輯的具體實現:如具體的登錄代碼在這里編寫* 這里面有一個回調* @author 白乾濤*/public class UserBiz implements IUserBiz {@Overridepublic void login(final String username, final String password, final OnLoginListener onLoginListener) {new Thread() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if ("username".equals(username) && "password".equals(password)) {User user = new User();user.setUsername(username);user.setPassword(password);//成功時的回調onLoginListener.loginSuccess(user);} else {//失敗時的回調onLoginListener.loginFailed();}}}.start();}}
/*** 這個其實不算是MVP中的東西(但一般都會有),這是一個回調,當執行復雜邏輯的同時做一些回調處理* @author 白乾濤*/public interface OnLoginListener {public void loginSuccess(User user);public void loginFailed();}