在Android開發中,我們通常會去將項目分成一個個的模塊文件夾,來進行管理維護,有的人是直接按照功能來分模塊,這也是最常見的,有的人則會按照一定的設計模式,再結合功能來進行項目模式設計,比如MVP、MVVM這兩種目前比較流行的項目設計模式,本文主要講解MVP模式。
MVC、MVP、MVVM
MVC
對於MVC我想大家應該都不陌生,最典型的MVC就是JSP + servlet + javabean的模式了。


這兩張是我從百度中截來的圖(原諒我太懶,不想畫圖),從中很容易看出MVC
的大致流程,用戶通過操作View
來發送用戶請求;Controller
接收到 用戶請求 后,負責決定應該調用哪個Model
來進行處理;然后Model
根據用戶請求進行相應的業務邏輯處理,並返回數據;最后Controller
調用相應的視圖View
來顯示Model
返回的數據。
MVP

首先,我們來看一下上圖(沒錯,又是從百度上摳下來的?_?),View
發送指令給Presenter
,Presenter
獲取指令后,調用響應的Model
進行業務邏輯處理,然后返回數據給Presenter
,Presenter
根據Model
返回的數據再來調用相應的View
。
MVVM

看上圖(盜圖狂魔就是我@_@),在MVVM
中有個ViewModel
,它的作用就是與View
進行雙向綁定,當View
或者ViewModel
有一方變動時,另一方也會跟着改變,其實就是觀察者模式,同時ViewModel
也會處理一些輕量的業務邏輯,具有和MVP
中的Presenter
的一些類似功能。當用戶對View
進行操作時,ViewModel
就會直接收到指令,然后調用Model
處理業務邏輯,當Model
返回數據給ViewModel
時,因為ViewModel
與View
雙向綁定的緣故,ViewModel
接收到數據后,View
也會跟着改變,省去了ViewModel
特意調用View
來改變View
的狀態這一步驟!有興趣的同學,可以去體驗一下Android Studio
中的databinding
簡單感受一下。
為什么用MVP
在Android中,對於Activity並沒有明確的說它是屬於View還是Controller的范疇,Activity既有View的性質,也具有Controller的性質,所以導致MVC在Android中很難明確分工使用,導致Activity很重。而且MVC中View會與Model直接交互,所以Activity與Model的耦合性很高,當后期維護時,稍有變動,可能Model、Activity、XML都會跟着改變,工作量很大,成本太高。
而MVP與MVC最大的不同之處是,MVP將M與V分隔開來,通過P交互,這樣在Android中,就可以明確的把Activity當作View處理,雖然可能還有一點邏輯在其中,但是已經無傷大雅;View和Model不直接交互,當View有變動或者Model有變動時,不會相互影響,有太大變動,,耦合性低,對於后期維護來說,特別是項目越來越龐大時,可以很快的理清項目結構,找到需要修改的地方,大大的縮短了工作量。而且,因為View與Model分離的緣故,Model可以單獨進行單元測試。
對於MVVM,其實很多框架中都使用到了它,比如說Vue.js、AngularJs都使用到了這種模式,在Android中也有DataBinding這個原生插件可以使用,來達到雙向綁定的作用,但只是使用了DataBinding並不是完全的MVVM模式,個人認為還必須有個中間層類似與Presenter一樣的層,來處理一些交互;說實話,在Android中使用了MVVM后,的確大大的提高了開發效率,省去了很多代碼,但是如果只是使用純粹的MVVM,當項目變得龐大后,還是有些吃不消的,各種改動的工作量不是鬧着玩的,所以個人認為MVVM只是適合中小型項目,對於大項目還是有點吃緊的。
所以,最后個人認為如果你的項目會越來越龐大,但是又想體驗MVVM那種便利,不妨試試MVP+DataBinding,其實這就有點類似於MVPVM模式了,方便快捷,即使項目龐大,改動時也不需要太多重構。
MVPLoader
好了,上面說了那么多,我們還是來點實際的吧,下面是本人在項目中對MVP的處理方式,有不同見解的,歡迎大家提出。
==================================View======================================= 所有的view(Activity、FragmentActivity、Fragment...)都必須實現這個接口 public interface IView { // 此方法是為了當Presenter中需要獲取上下文對象時,傳遞上下文對象,而不是讓Presenter直接持有上下 文對象 Activity getSelfActivity(); } 這是Activity的基類: public abstract class BaseActivity<P extends IPresenter> extends Activity implements IView { // Presenter對象 protected P MvpPre; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); MvpPre = bindPresenter(); } // 綁定Presenter protected abstract P bindPresenter(); public <T> T $(int resId) { return (T) findViewById(resId); } public <T> T $(int resId, View parent) { return (T) parent.findViewById(resId); } @Override public Activity getSelfActivity() { return this; } @Override protected void onDestroy() { super.onDestroy(); /** * 在生命周期結束時,將presenter與view之間的聯系斷開,防止出現內存泄露 */ if (MvpPre != null) { MvpPre.detachView(); } } } ==================================Presenter======================================= public interface IPresenter { void detachView(); } Presenter的基類: public abstract class BasePresenter<V extends IView> implements IPresenter { // 此處使用弱引用是因為,有時Activity關閉不一定會走onDestroy,所以這時使用弱引用可以及時回收IView protected Reference<V> MvpRef; public BasePresenter(V view) { attachView(view); } private void attachView(V view) { MvpRef = new WeakReference<V>(view); } protected V getView() { if (MvpRef != null) { return MvpRef.get(); } return null; } /** * 主要用於判斷IView的生命周期是否結束,防止出現內存泄露狀況 * * @return */ protected boolean isViewAttach() { return MvpRef != null && MvpRef.get() != null; } /** * Activity生命周期結束時,Presenter也清除IView對象,不在持有 */ @Override public void detachView() { if (MvpRef != null) { MvpRef.clear(); MvpRef = null; } } } ===================================demo======================================== 接口: /** * 創建一個類作為紐帶,將view、presenter、model的接口方法都串聯在一起,更加便於管理 */ public final class MainContacts { public interface IMain extends IView { void showTips(boolean isSucceess); } public interface IMainPre extends IPresenter { void login(String username, String password); } public interface IMainLgc { boolean login(String username, String password); } } Model部分: public class MainLogic implements MainContacts.IMainLgc { public boolean login(String username, String password) { if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) { return false; } return true; } } View部分: public class MainActivity extends BaseActivity<MainPresnter> implements MainContacts.IMain { private EditText editT_username, editT_password; private Button btn_login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); addListeners(); } @Override protected MainPresnter bindPresenter() { return new MainPresnter(this); } private void initUI() { editT_username = $(R.id.editT_username); editT_password = $(R.id.editT_password); btn_login = $(R.id.btn_login); } private void addListeners() { btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MvpPre.login(editT_username.getText().toString(), editT_password.getText().toString()); } }); } @Override public void showTips(boolean isSucceess) { if (isSucceess) { Toast.makeText(this, "登錄成功!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "登錄失敗!", Toast.LENGTH_SHORT).show(); } } } Presenter部分: public class MainPresnter extends BasePresenter<MainContacts.IMain> implements MainContacts.IMainPre { private MainLogic mMainLogic; public MainPresnter(MainContacts.IMain view) { super(view); this.mMainLogic = new MainLogic(); } @Override public void login(String username, String password) { // 判斷activity的生命周期是否結束,不判斷的話在極端情況下可能會出現內存泄露 if (isViewAttach()) { MvpRef.get().showTips(mMainLogic.login(username, password)); } } }
好了,以上便是本人對MVP模式的一些理解,如果你不想自己在重新搭建MVP框架,可以直接使用MVPLoader項目做依賴:
Step 1: allprojects { repositories { ... maven { url 'https://jitpack.io' } } } Step 2: dependencies { compile 'com.github.albert-lii:MVPLoader:1.0.6' }
如果你有不同的理解,也歡迎提出,大家一起進步。
Github地址
https://github.com/albert-lii/MVPLoader
如果覺得不錯,給個贊吧
作者:albertlii
鏈接:https://www.jianshu.com/p/479aca31d993
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。