Android架構篇--MVP模式的介紹篇
摘要: 在MVVM成熟之前MVP模式在Android上有被神化的趨勢,筆者曾經在商業項目中從零開始大規模采用過MVP模式對項目進行開發。在使用MVP模式進行開發的時候發現項目的結構模式對開發是有一定的影響的,在這裏筆者會對這一問題進行探討。希望通過這篇blog能讓讀者了解如何使用MVP模式搭建一個功能完善的MVP模式開發框架,避免一些筆者認為比較嚴重的問題。
為什麽要使用MVP模式
在傳統的Android開發中,我們一般是使用MVC模式進行開發的。
傳統MVC模式介紹:
- View: 視圖層,對應xml文件
- Controller: 控制層,對應Activity和Fragment層,進行數據處理
- Model:實體層,負責獲取實體數據
在Android開發中采用MVC模式的一個最大的弊端就是xml作為View層視圖能力實在太弱,所以一般情況下我們都是通過Controller層來輔助處理一些視圖的。這樣的結果就導致Controller既作為控制層的同時又承擔了View層的大部分功能,采用MVC模式往往會導致Activity和Fragment中的代碼非常復雜。我們將Android中采用的MVC模式稱為MV模式更加恰當。
MVP模式介紹:
- View: 視圖層,對應xml文件與Activity/Fragment
- Presenter: 邏輯控制層,同時持有View和Model對象
- Model: 實體層,負責獲取實體數據
MVP模式的流程圖如下:
采用MVP模式的優勢是:
- 把業務邏輯抽離到Presenter層中,View層專註於UI的處理。
- 分離視圖邏輯與業務邏輯,達到解耦的目的。
- 提高代碼的閱讀性。
- Presenter被抽象成接口,可以根據Presenter的實現方式進行單元測試。
- 可拓展性強。
采用MVP模式的缺點:
- 項目結構會對後期的開發和維護有一定的影響。具體視APP的體量而定。
- 代碼量會增多,如何避免編寫過多功能相似的重復代碼是使用MVP開發的一個重點要處理的問題。
- 有一定的學習成本。
綜上所述,在Android上采用MVP模式的優勢是:大大優化代碼的維護性與拓展性的同時對代碼進行深度解耦,使各個層級的分工更加明晰。
Android上MVP模式的簡單應用
先來看看一個簡單用mvp模式模擬登陸的demo,下面的示例代碼和其它簡單介紹MVP模式的代碼沒有太大區別。如果有了解過的同學可以直接跳過看下一章關於如何優化MVP模式的結構的文章。
下面我們來看看在Android上用MVP模式實現簡單的登錄邏輯的方式:
. 登陸界面
- 項目的結構:
從上面的代碼結構圖可看出,用MVP模式實現登陸模塊需要創建6個文件,分別是M、V、P接口文件和接口的對應實現。其中LoginActivity就是View層的具體實現。這樣的好處時Activity組件只需要負責處理UI相關邏輯就可以了,而相關的業務邏輯全部抽象到Presenter層中處理。通過這種方式能夠很好的避免傳統Android開發中的Activity/Fragment等UI組件既負責處理UI邏輯又處理業務邏輯的結果。
. 代碼實現
說了這麽多,最後我們來看看代碼的實現吧。
-
ILoginModel
1 2 3 public interface ILoginModel { void login(String name ,String password); } -
ILoginPresenter
1 2 3 4 5 6 public interface ILoginPresenter { void loginToServer(String userName,String password); void loginSucceed(); } -
ILoginView
1 2 3 4 5 6 public interface ILoginView { void showProgress(boolean enable); void showLoginView(); }
上面是登陸模塊對應的MVP接口的具體設計,下面我來簡單介紹一下接口中的幾個方法:
ILoginModel.login(String name ,String password)
登陸方法,通過該方法向服務器發送登陸請求。ILoginPresenter. loginToServer (String name ,String password)
通知Model響應登陸事件。ILoginPresenter. loginSucceed()
當登陸事件完成時(成功/失敗),Model層要通知該方法登陸事件已完成。ILoginView. showProgress(boolean enable)
當Presenter層調用loginToServer (String name ,String password)
方法時,要通過該方法通知View層顯示加載動畫。ILoginView. showLoginView()
登陸成功時,Presenter層會通過該方法通知View層登陸已成功。
下面我們來看看這幾個接口的具體實現。
- LoginModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class LoginModel implements ILoginModel{ private ILoginPresenter presenter; private Handler mHandler = new Handler(); public LoginModel(ILoginPresenter presenter) { this.presenter = presenter; } public void login(String name ,String password) { mHandler.postDelayed(new Runnable() { public void run() { Log.d("LoginModel", "run: "); presenter.loginSucceed(); } },2000); } }
上面的Model層實現了login(String name,Stringpassword)
登陸方法,該方法的具體實現邏輯是通過線程休眠2秒來模擬網絡登陸的過程,登陸成功後會通過LoginPresenter的loginSucceed()
方法來通知Presenter層登陸結果。實際開發中我們需要根據具體的業務邏輯來實現該過程。
- LoginPresenter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class LoginPresenter implements ILoginPresenter{ private ILoginModel loginModel; private ILoginView loginView; public LoginPresenter(ILoginView loginView) { this.loginView = loginView; this.loginModel = new LoginModel(this); } public void loginToServer(String userName, String password) { loginView.showProgress(true); loginModel.login(userName,password); } public void loginSucceed() { loginView.showProgress(false); loginView.showLoginView(); } }
從上面代碼可以看出LoginPresenter的實現邏輯很簡單,首先在構造方法中獲取ILoginView對象並撞見ILoginModel對象。然後當View層調用loginToServer(String userName, String password)
方法成功時,通知View層顯示加載動畫並調用ILoginModel層的login(String userName, String password)
方法向服務器發送登陸請求。當登陸成功後(即Model層通知loginSucceed方法時)通過loginView.showProgress(false)
方法通知View層隱藏加載動畫,並通知View登陸成功。
- LoginActivity
對於LoginActivity我們只需要關註其中的幾個方法即可1 2 3 4 5 6 7 loginBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //模擬登陸,不需要賬號密碼 loginPresenter.loginToServer("",""); } });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Override public void showProgress(boolean enable) { if (enable){ progressBar.setVisibility(View.VISIBLE); loginLayout.setVisibility(View.GONE); }else { progressBar.setVisibility(View.GONE); loginLayout.setVisibility(View.VISIBLE); } } @Override public void showLoginView() { Toast.makeText(LoginActivity.this,"登陸功",Toast.LENGTHSHORT).show(); finish(); } |
上面時實現了ILoginView接口的兩個方法。
結合上面的代碼可以看出,當點擊登陸按鈕的監聽事件時,我們不需要關註業務邏輯,只需要調用loginPresenter.loginToServer("","");
方法即可,然後根據實際情況實現View層中ILoginView接口的方法即可,這樣達到了UI業務與邏輯完全分離的目的。
Android架構篇--MVP模式的介紹篇