淺談Andorid開發中的MVP模式
來自: http://www.jcodecraeer.com//a/anzhuokaifa/androidkaifa/2016/0225/3994.html
導語:最近公眾號后臺經常收到一些消息,說能不能講一些開發模式,經過思考后,我決定講一講MVP模式。希望對大家能夠有所幫助。并寫了一個簡單的小demo。
背景
看到MVP,大家肯定會想什么是MVP呢?這個我可以肯定的告訴大家MVP(Most Valuable Player)是最有價值球員的意思,這當然是開玩笑了。之所以會出現MVP這種架構模式,是因為我相信大家在開發App時,肯定會發現,Activity的負擔非常重,既要初始化控件,又要寫一些邏輯操作的展示等等,有時候很多Activity中的代碼都充當了Controller和Model的角色,所以你會發現Activity違背單一職責原則,負擔過重。所以,就出現了這么一種架構模式,叫MVP,并不是最有價值球員哦。
什么是MVP架構
MVP就是Model-View-Presenter,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是如何解決MVC的問題?
MVP架構:
View: 對應于Activity,負責View的繪制以及與用戶交互
Model: 依然是業務邏輯和實體模型
Presenter: 負責完成View于Model間的交互
-
View不直接與Model交互,而是通過與Presenter交互來與Model間接交互。
-
Presenter與View的交互是通過接口來進行的。
-
通常View與Presenter是一對一的,但復雜的View可能綁定多個Presenter來處理邏輯。
MVC架構:
View:對應于布局文件
Model:業務邏輯和實體模型
Controllor:對應于Activity
-
View可以與Model直接交互。
-
Controller是基于行為的,并且可以被多個View共享。
-
可以負責決定顯示哪個View。
總結解釋一下就是說:從MVC到MVP的一個轉變,就是減少了Activity的職責,減輕了它的負擔,簡化了Activity中的代碼和一些操作,將邏輯代碼提取到了Presenter中進行處理,降低了其耦合度。
進一步的解釋:
在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.降低耦合度,隱藏數據,Activity中代碼更簡潔
2.模塊職責劃分明顯
3.方便測試驅動開發
4.代碼復用度較高
5.代碼靈活性
MVP架構模式實例
這個實例是根據用戶id獲取用戶信息并展示的一個過程,其中獲取用戶信息用了一個線程進行了模擬獲取。希望大家能夠看懂,并對大家有所幫助。
我們先看一下MVP目錄結構圖
1、Model層
首先是一個javabean User實體類
package net.loonggg.mvpdemo.bean;public class User { private String name; private String id; private String sex; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
Model層抽象接口實現:
package net.loonggg.mvpdemo.model;import net.loonggg.mvpdemo.bean.User;public class GetUserInfo implements IGetUser { @Override public void getUserInfo(final int id, final OnUserInfoListener listener) { // 模擬子線程耗時操作 new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 模擬信息獲取成功 if (id == 1) { User user = new User(); user.setName("非著名程序員"); user.setAge("26"); user.setSex("男"); user.setId("1"); listener.getUserInfoSuccess(user); } else { listener.getUserInfoFailed(); } } }.start(); } }
Model層抽象接口
package net.loonggg.mvpdemo.model;public interface IGetUser { public void getUserInfo(int id, OnUserInfoListener listener); }
package net.loonggg.mvpdemo.model;import net.loonggg.mvpdemo.bean.User;public interface OnUserInfoListener { void getUserInfoSuccess(User user); void getUserInfoFailed(); }
2、View層
我們都知道Presenter與View交互是通過接口,所以我們需要定義一個IShowUserView的接口,這個接口封裝的方法基本上都跟視圖展示有關。
package net.loonggg.mvpdemo.view;import net.loonggg.mvpdemo.bean.User;public interface IShowUserView { void showLoading(); void hideLoading(); void toMainActivity(User user); void showFailedError(); }
3、Presenter層
Presenter是Model和View之間交互的橋梁,里面有一些業務邏輯的操作。
public class UserInfoPresenter { private IGetUser iGetUser; private IShowUserView iShowUserView; private Handler mHandler = new Handler(); public UserInfoPresenter(IShowUserView iShowUserView) { this.iShowUserView = iShowUserView; this.iGetUser = new GetUserInfo(); } public void getUserInfoToShow(int id) { iShowUserView.showLoading(); iGetUser.getUserInfo(id, new OnUserInfoListener() { @Override public void getUserInfoSuccess(final User user) { // 需要在UI線程執行 mHandler.post(new Runnable() { @Override public void run() { iShowUserView.toMainActivity(user); iShowUserView.hideLoading(); } }); } @Override public void getUserInfoFailed() { mHandler.post(new Runnable() { @Override public void run() { iShowUserView.showFailedError(); iShowUserView.hideLoading(); } }); } }); } }
4、Activity中的調用
public class MainActivity extends Activity implements IShowUserView { private Button btn; private TextView name_tv, age_tv, sex_tv; private ProgressDialog pd = null; private UserInfoPresenter userInfoPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); userInfoPresenter = new UserInfoPresenter(this); btn = (Button) findViewById(R.id.btn); name_tv = (TextView) findViewById(R.id.name_tv); age_tv = (TextView) findViewById(R.id.age_tv); sex_tv = (TextView) findViewById(R.id.sex_tv); pd = new ProgressDialog(this); pd.setMessage("正在加載……"); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { userInfoPresenter.getUserInfoToShow(1); } }); } @Override public void showLoading() { pd.show(); } @Override public void hideLoading() { pd.cancel(); } @Override public void toMainActivity(User user) { name_tv.setText(user.getName()); age_tv.setText(user.getAge()); sex_tv.setText(user.getSex()); } @Override public void showFailedError() { Toast.makeText(this, "獲取信息有誤", Toast.LENGTH_SHORT).show(); } }
結語:看完實例代碼,有點感覺了吧?俗話說好記性不如爛筆頭,看不如寫,試著自己去寫一個,領會一下其中的精神,相信你會豁然開朗。當然有人說這么做,是不是又多了一層,感覺又麻煩了,是嗎?降低了耦合度,提取了代碼,并增加了復用,代碼更簡潔,其實好處還是很多的。
移動開發者的聚集地,公眾號“非著名程序員”,每天一篇原創技術分享和移動互聯網知識分享,微信公眾號:smart_android