Android開發中的MVP模式詳解


在Android開發中,我們通常會去將項目分成一個個的模塊文件夾,來進行管理維護,有的人是直接按照功能來分模塊,這也是最常見的,有的人則會按照一定的設計模式,再結合功能來進行項目模式設計,比如MVP、MVVM這兩種目前比較流行的項目設計模式,本文主要講解MVP模式。

MVC、MVP、MVVM

MVC

對於MVC我想大家應該都不陌生,最典型的MVC就是JSP + servlet + javabean的模式了。


 
MVC1
 
MVC2

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

MVP

 
MVP

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

MVVM

 
MVVM

看上圖(盜圖狂魔就是我@_@),在MVVM中有個ViewModel,它的作用就是與View進行雙向綁定,當View或者ViewModel有一方變動時,另一方也會跟着改變,其實就是觀察者模式,同時ViewModel也會處理一些輕量的業務邏輯,具有和MVP中的Presenter的一些類似功能。當用戶對View進行操作時,ViewModel就會直接收到指令,然后調用Model處理業務邏輯,當Model返回數據給ViewModel時,因為ViewModelView雙向綁定的緣故,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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM