架構 MVC MVP MVVM 簡介 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

MVC 架構

MVC各層的做用

  • M層:Model,SQL、XML、JSON,數據模型。負責與數據處理相關的業務邏輯的處理,好比數據庫讀寫操做,網絡請求操做,複雜的算法,耗時的任務等。Model是一個應用系統的核心部分,表明了該系統實際要實現的全部功能。當M層完成數據處理後,會通知Controller更新View。
  • V層:View,XML佈局、自定義View,Java編寫的View。負責在屏幕上渲染出相應的圖形信息展現給用戶看。
  • C層:Controlle,Activity或者Fragmentr,控制器。負責接收如點擊、觸摸、電話呼入、網絡改變等外部事件,並向Model層發送數據請求。同時負責接收Model層處理完數據後發的通知,並更新View。Controller是View和Model之間通訊的橋樑。

Android中的實際狀況

其實Android中只有MVandroid

Android下MVC中的控制層是由Activity來承擔的,Activity原本主要是做爲初始化頁面,展現數據的操做,可是由於XML視圖功能太弱,因此Activity既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多。git

好比對於登陸頁面,MVC的基本流程爲:github

用戶與View交互,View接收並反饋用戶的動做,View把用戶的請求傳給相應的控制器,由控制器決定調用哪一個模型,而後由模型調用相應的業務邏輯對用戶請求進行加工處理,若是須要返回數據,模型會把相應的數據返回給控制器,由控制器調用相應的視圖,最終由視圖渲染返回的數據。算法

在Android開發中,Activity並非一個標準的MVC模式中的Controller,它的首要職責是加載應用的佈局和初始化用戶界面,接受並處理來自用戶的操做請求,進而做出響應。隨着界面及其邏輯的複雜度不斷提高,Activity類的職責不斷增長,以至變得龐大臃腫。數據庫

好比在Android中,對於登陸頁面,典型的交互過程是這樣的:後端

用戶點擊登陸按鈕 → Activity中註冊的監聽器檢測到點擊事件 → Activity經過轉動View中的ProgressBar來響應用戶點擊事件 → Activity經過View中的EditText獲取用戶輸入的帳戶密碼 → Activity將數據交由業務邏輯層(Model層)處理 → Model層處理完成後經過回調將數據返回給Activity → Activity更新UI反饋給用戶設計模式

由上面的案例能夠看出,其實這個View對應於佈局文件能作的事情特別少,實際上關於該佈局文件中的數據綁定、事件處理的代碼都在Activity中,形成了Activity既像View又像Controller,這可能也就是爲什麼:緩存

Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.微信

演示案例

BaseModel

BaseModel顧名思義就是全部業務邏輯model的父類,這裏的onDestroy()方法用於跟activity或者fragment生命週期同步,在destroy作一些銷燬操做網絡

public interface BaseModel {
    void onDestroy();
}

Callback

Callback是根據View或者Controller調用Model時回調的參數個數選擇使用

public interface Callback1<T> {
    void onCallBack(T t);
}
public interface Callback2<T, P> {
    void onCallBack(T t, P p);
}

SampleModel

SampleModel是咱們業務邏輯的具體實現

public class SampleModel implements BaseModel {

    public void getUserInfo(String uid, Callback1<UserInfo> callback) {
        UserInfo userInfo = new UserInfo();
        //...從網絡或數據庫等獲取數據
        callback.onCallBack(userInfo);
    }

    public void getUserInfo2(String uid, Callback2<UserInfo, String> callback) {
        UserInfo userInfo = new UserInfo();
        //...從網絡或數據庫等獲取數據
        callback.onCallBack(userInfo, "其餘數據");
    }

    @Override
    public void onDestroy() {

    }

    public class UserInfo {
        private int age;
        private String name;
        //...
    }
}

SampleActivity

public class SampleActivity extends AppCompatActivity {
    private SampleModel sampleModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);
        sampleModel = new SampleModel();
        String uid = "123456";
        findViewById(R.id.button).setOnClickListener(view -> getUserInfo(uid));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        sampleModel.onDestroy();
    }

    //獲取用戶信息
    private void getUserInfo(String uid) {
        sampleModel.getUserInfo(uid, userInfo -> {
            //...設置用戶信息到view
        });
    }
}

案例總結

事件的流向

  • button點擊事件的觸發:View → Controller
  • 獲取用戶信息事件的觸發:Controller → Model → Controller
  • 綁定用戶信息到View:Controller → View

總結

  • 具備必定的分層,model完全解耦,controller和view並無解耦
  • 層與層之間的交互儘可能使用回調或者去使用消息機制去完成,儘可能避免直接持有
  • controller和view在android中沒法作到完全分離,但在代碼邏輯層面必定要分清
  • 業務邏輯被放置在model層,可以更好的複用和修改增長業務

MVP 架構

MVP的核心就是:讓M和V徹底解耦,經過Presenter統一調度管理

MVP的基本流程:Presenter從View中獲取須要的參數,交給Model去處理,Model執行過程當中的反饋以及結果告訴Presenter,Presenter再讓View作對應的顯示。

基本概念

MVP跟MVC很相像,根據MVC的發展來看,咱們把MVP當成MVC來看也不爲過,由於MVP也是三層,惟一的差異是Model和View之間不進行通信,都是經過Presenter完成。

前面介紹MVC的時候提到了算是致命缺點吧,在android中因爲activity(god object)的存在,Controller和View很難作到徹底解耦。但在MVP中就能夠很好的解決這個問題

特色

  • 是MVC的演化版本
  • 讓Model和View徹底解耦,由Presenter負責完成View與Model的交互
  • 將Actvity視爲View層,減小了Activity的職責,將複雜的邏輯代碼提取到了Presenter中
  • Presenter與View之間的交互徹底是經過接口的
  • 代碼很清晰,不過增長了不少類;耦合度更低,更方便的進行測試;有助於協同開發,下降維護成本

MVP各層的做用

  • Model:數據模型,和MVC中的Model同樣
  • View:對應UI界面(包括Activity、Fragment、以及全部視圖),負責View的繪製以及與用戶交互
  • Presenter:調度者,負責完成View和Model間的交互

用MVP架構編寫登陸模塊完整版

定義Presenter接口(可選)

分析這個模塊須要哪些業務邏輯,或者說有哪些複雜的功能,以此定義Presenter接口。
對於登陸模塊,主要的就是登陸功能。爲了增長接口的複雜度,這裏我又添加了一個退出前清理功能。

public interface Login_Presenter_I {
    void login(String username, String password);//登陸過程可能涉及到很工做,因此把它抽出來。此過程須要與View交互

    void onFinishActivity();//退出前可能要作不少清理工做,因此也把它抽出來。此過程不須要與View交互
}

定義Model接口(可選)及MP回調接口(必選)

分析上述Presenter層中的功能在被Model層處理過程當中,Model須要通知Presenter哪些內容,以此定義Model接口。
Model層在執行過程當中,是經過接口通知Presenter執行過程當中的狀態以及執行完畢後的結果的,爲了邏輯更清晰,建議此此接口定義爲Model層接口的內部接口。

public interface Login_Model_I {
    //參數 listener:Model層經過此接口通知Presenter執行過程當中的狀態以及執行完畢後的結果
    void login(String username, String password, OnLoginListener listener);//執行過程當中須要通知Presenter

    void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//執行過程當中不須要通知Presenter

    //將Model層須要通知Presenter的內容,按照類別,定義在不一樣的接口中
    interface OnLoginListener {
        void onUsernameError();
        void onPasswordError();
        void onSuccess();
    }
}

定義View接口(必選)

分析全部可能會操做UI的最基礎邏輯,以此定義View接口。
Presenter層是經過接口來操做View層的。

public interface Login_Activity_I {
    void showProgress();
    void hideProgress();
    void setUsernameError();
    void setPasswordError();
    void showToast(String msg, int duration);
}

定義Presenter的實現類

public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener {

    private Login_Activity_I loginActivityI; //拿到的是接口,整個Presenter中沒有導入任何View和Activity
    private Login_Model_I loginIModel; //拿到的是Model層的實現類

    public Login_Presenter_Impl(Login_Activity_I loginActivityI) {
        this.loginActivityI = loginActivityI;//View層的實現類,由Activity傳過來
        this.loginIModel = new Login_Model_Impl();//Model層的實現類,由Activity傳過來或本身建立都可
    }

    @Override
    public void login(String username, String password) {
        if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
        loginIModel.login(username, password, this);//交給Model層處理
    }

    @Override
    public void onFinishActivity() {
        if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
        boolean clearSp = new Random().nextBoolean();
        boolean deleteCache = new Random().nextBoolean();
        loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交給Model層處理
        if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI
    }

    @Override
    public void onUsernameError() {
        if (loginActivityI != null) {
            loginActivityI.setUsernameError();
            loginActivityI.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (loginActivityI != null) {
            loginActivityI.setPasswordError();
            loginActivityI.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (loginActivityI != null) loginActivityI.showToast("登陸成功", Toast.LENGTH_SHORT);
    }
}

定義Model的實現類

Model層的實現類,只處理邏輯,徹底不操做View,對邏輯處理的狀態反饋給Presenter

public class Login_Model_Impl implements Login_Model_I {
    @Override
    public void login(String username, String password, OnLoginListener listener) {
        if (TextUtils.isEmpty(username)) listener.onUsernameError();
        else if (TextUtils.isEmpty(password)) listener.onPasswordError();
        else listener.onSuccess();
    }

    @Override
    public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) {
        if (clearSp) Log.i("bqt", "【清理SP】");
        if (deleteCache) Log.i("bqt", "【清理緩存】");
    }
}

讓Activity實現View接口

public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;

    private Login_Presenter_I presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);

        presenter = new Login_Presenter_Impl(this);//new一個Presenter的實現類,把本身傳過去。實際上接收的只是LoginView接口的實例
    }

    @Override
    public void finish() {
        presenter.onFinishActivity();
        super.finish();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void showToast(String msg, int duration) {
        Toast.makeText(this, msg, duration).show();
    }

    @Override
    public void onClick(View v) {
        //點擊登陸時,View把數據傳給presenter,presenter處理完數據後通知View處理事件
        presenter.login(username.getText().toString(), password.getText().toString());
    }
}

用MVP架構編寫登陸模塊簡潔版

View層接口

public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

Activity

public class LoginActivity extends AppCompatActivity implements LoginView {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = findViewById(R.id.progress);
        username = findViewById(R.id.username);
        password = findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(v -> login());

        presenter = new LoginPresenter(this, new LoginModel());
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError("用戶名錯誤");
    }

    @Override
    public void setPasswordError() {
        password.setError("密碼錯誤");
    }

    @Override
    public void navigateToHome() {
        startActivity(new Intent(this, MainActivity.class));
        finish();
    }

    private void login() {
        presenter.login(username.getText().toString(), password.getText().toString());
    }
}

Presenter

public class LoginPresenter implements LoginModel.OnLoginFinishedListener {

    private LoginView LoginView;
    private LoginModel loginModel;

    LoginPresenter(LoginView LoginView, LoginModel loginModel) {
        this.LoginView = LoginView;
        this.loginModel = loginModel;
    }

    public void login(String username, String password) {
        if (LoginView != null) {
            LoginView.showProgress();
        }
        loginModel.login(username, password, this);
    }

    public void onDestroy() {
        LoginView = null;
    }

    @Override
    public void onUsernameError() {
        if (LoginView != null) {
            LoginView.setUsernameError();
            LoginView.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (LoginView != null) {
            LoginView.setPasswordError();
            LoginView.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (LoginView != null) {
            LoginView.navigateToHome();
        }
    }
}

Model

public class LoginModel {

    interface OnLoginFinishedListener {
        void onUsernameError();

        void onPasswordError();

        void onSuccess();
    }

    public void login(final String username, final String password, final OnLoginFinishedListener listener) {
        new Handler().postDelayed(() -> {
            if (TextUtils.isEmpty(username)) {
                listener.onUsernameError();
                return;
            }
            if (TextUtils.isEmpty(password)) {
                listener.onPasswordError();
                return;
            }
            listener.onSuccess();
        }, 2000);
    }
}

MVVM 架構

MVP中,隨着業務邏輯的增長,UI的改變多的狀況下,會有很是多的跟UI相關的case,這樣就會形成View的接口會很龐大。而MVVM就解決了這個問題,經過雙向綁定的機制,實現數據和UI內容,只要想改其中一方,另外一方都可以及時更新的一種設計理念,這樣就省去了不少在View層中寫不少case的狀況,只須要改變數據就行。

先看下MVVM設計圖:

這看起來跟MVP好像沒啥差異,其實區別仍是挺大的,在MVP中,View和presenter要相互持有,方便調用對方,而在MVP中,View和ViewModel經過Binding進行關聯,他們以前的關聯處理經過DataBinding完成。

MVVM與DataBinding的關係用一句話表述就是,MVVM是一種思想,DataBinding是谷歌推出的方便實現MVVM的工具

在google推出DataBinding以前,由於xml layout功能較弱,想實現MVVM很是困難。而DataBinding的出現可讓咱們很方便的實現MVVM。

看起來MVVM很好的解決了MVC和MVP的不足,可是因爲數據和視圖的雙向綁定,致使出現問題時不太好定位來源,有可能數據問題致使,也有可能業務邏輯中對視圖屬性的修改致使。若是項目中打算用MVVM的話能夠考慮使用官方的架構組件ViewModel、LiveData、DataBinding去實現MVVM。

關於ViewModel、LiveData、DataBindin這些類或框架的使用,由於涉及到的內容比較多,這裏不詳細介紹。

如何選擇

前面在介紹MVC、MVP、MVVM時並無去詳細列出他們的優缺點,主要緣由是:關於架構,設計,模塊化等等,它們的優缺點沒有絕對的,主要看實現者如何去作

好比在mvp中咱們要實現根據業務邏輯和頁面邏輯作不少Present和View的具體實現,若是這些case太多,會致使代碼的可讀性變差。可是經過引入contract契約類,會讓業務邏輯變得清晰許多。所以不論是用哪一種設計模式,只要運用得當,均可以達到想要的結果。

若是非要說怎麼選的話,通常的建議以下:

  • 若是項目簡單,沒什麼複雜性,將來改動也不大的話,那就不要用設計模式或者架構方法,只須要將每一個模塊封裝好,方便調用便可,不要爲了使用設計模式或架構方法而使用。
  • 對於偏向展現型的app,絕大多數業務邏輯都在後端,app主要功能就是展現數據,交互等,建議使用mvvm
  • 對於工具類或者須要寫不少業務邏輯app,使用mvp或者mvvm均可。
  • 若是想經過一個項目去學習架構和設計模式,建議用MVC而後在此基礎上慢慢挖掘改進。最後你可能發現,改進的最終結果可能就變成了mvp,mvvm。

2019-5-9

相關文章
相關標籤/搜索