天天看點

Android 架構 MVC MVP MVVM,這一波你應該了然于心

MVC,MVP和MVVM是軟體比較常用的三種軟體架構,這三種架構的目的都是分離,避免将過多的邏輯全部堆積在一個類中。

在Android中,Activity中既有UI的相關處理邏輯,又有資料擷取邏輯,進而導緻Activity邏輯複雜不單一難以維護。

為了一個應用可以更好的維護和擴充,我們需要很好的區分相關層級,要不然以後将資料擷取方式從資料庫變為網絡擷取時,我們需要去修改整個Activity。架構使得View和資料互相獨立,我們把應用分成三個不同層級,這樣我們就能夠單獨測試相關層級,使用架構能夠把大多數邏輯從Activity中移除,友善進行單元測試。

MVC是什麼?

MVC是模型(Model)-視圖(View)-控制器(Controller)的縮寫,用一種業務邏輯、資料、界面顯示分離的方法組織代碼。其實Android Studio建立一個項目的模式就是一個簡化的mvc模式。

Android中的MVC含義

Model:實體類(資料的擷取、存儲、資料狀态變化)。

View:布局檔案

Controller:Activity(處理資料、業務和UI)。

工作原理

Android 架構 MVC MVP MVVM,這一波你應該了然于心

View接受使用者的互動請求。

View将請求轉交給Controller。

Controller操作Model進行資料更新。

資料更新之後,Model通知View資料變化。

View顯示更新之後的資料。

MVC的缺點

随着界面及其邏輯的複雜度不斷提升,Activity類的職責不斷增加,以緻變得龐大臃腫。

為了解決MVC的缺點,MVP 架構被提出來。

MVP是什麼

MVP是MVC架構的一個演化版,全稱是Model-View-Presenter。将MVC中的V和C結合生成MVP中的V,引入新的夥伴Presenter。

Android中的MVP含義

Model:實體類(資料的擷取、存儲、資料狀态變化)。

View:布局檔案+Activity。

Presenter:中介,負責完成View與Model間的互動和業務邏輯。

工作原理

Android 架構 MVC MVP MVVM,這一波你應該了然于心

View 接收使用者互動請求

View 将請求轉交給 Presenter(V調用P接口)

Presenter 操作Model進行資料更新(P調用M接口)

Model 通知Presenter資料發生變化(M調用P接口)

Presenter 更新View資料(P執行接口,V相應回調)

MVP的優點

複雜的邏輯處理放在Presenter進行處理,減少了Activity的臃腫。

解耦。Model層與View層完全分離,修改V層不會影響M層,降低了耦合性。

可以将一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。

Presenter層與View層的互動是通過接口來進行的,便于單元測試。

MVP的缺點

維護困難。Presenter中除了業務邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。

MVVM是什麼

是 Model-View-ViewModel 的簡寫。MVVM與MVP的結構還是很相似的,就是将Presenter更新為ViewModel。在MVVM中,View層和Model層進行了雙向綁定(即Data Binding),是以Model資料的更改會表現在View上,反之亦然。ViewModel就是用來根據具體情況處理View或Model的變化。

Android中的MVVM含義

Model:實體類(資料的擷取、存儲、資料狀态變化)。

View:布局檔案+Activity。

ViewModel: 關聯層,将Model和View進行綁定,Model或View更改時,實時重新整理對方。

工作原理

Android 架構 MVC MVP MVVM,這一波你應該了然于心

View 接收使用者互動請求

View 将請求轉交給ViewModel

ViewModel 操作Model資料更新

Model 更新完資料,通知ViewModel資料發生變化

ViewModel 更新View資料

View/Model的變動,隻要改其中一方,另一方都能夠及時更新到

MVVM的優點

1.提高可維護性。Data Binding可以實作雙向的互動,使得視圖和控制層之間的耦合程度進一步降低,分離更為徹底,同時減輕了Activity的壓力。

2.簡化測試。因為同步邏輯是交由Binder做的,View跟着Model同時變更,是以隻需要保證Model的正确性,View就正确。大大減少了對View同步更新的測試。

3.ViewModle易于單元測試。

MVVM的缺點

1.對于簡單的項目,使用MVVM有點大材小用。

2.對于過大的項目,資料綁定會導緻記憶體開銷大,影響性能。

3.ViewModel和View的綁定,使頁面異常追蹤變得不友善。有可能是View出錯,也有可能是ViewModel的業務邏輯有問題,也有可能是Model的資料出錯。

MVP和MVC的最大差別

在MVP中View并不直接使用Model,它們之間的通信是通過Presenter 來進行的,所有的互動都發生在Presenter内部,而在MVC中View直接從Model中讀取資料而不是通過 Controller。

如何選取架構

本來是要每個模式寫一個适用場景,最後想想每個人都有自己的了解,别被他人束縛了。

一句話:适合自己的才是最好的!

執行個體

Android 架構 MVC MVP MVVM,這一波你應該了然于心

就這麼一個界面咱通過MVC、MVP、MVVM分别搭建一下。

MVC執行個體

代碼結構

Android 架構 MVC MVP MVVM,這一波你應該了然于心

1.在layout建立一個布局檔案

<!--縮減版-->
    <LinearLayout
        ...>
        <EditText
            android:id="@+id/et_account"
            .../>
    </LinearLayout>
    <LinearLayout
        ...>
        <EditText
            android:id="@+id/et_password"
            .../>
    </LinearLayout>
    <Button
        android:id="@+id/btn_login"
        .../>
    <Button
        android:id="@+id/btn_back"
        .../>
           

2.實體類(User)

public class User {
    private String name;
    private String password;
    public User() {}
    //set or get ...
    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }
}
           

3.MVCLoginActivity

//使用者點選事件
mvcBinding.mcvLogin.btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                user.setName(mvcBinding.mcvLogin.etAccount.getText().toString());
                user.setPassword(mvcBinding.mcvLogin.etPassword.getText().toString());
                login(user);
            }
});
//邏輯處理
private void login(User user){
        if(!user.getName().isEmpty()&&!user.getPassword().isEmpty()){
            if(user.getName().equals("scc001")&&user.getPassword().equals("111111"))
            {
                Toast.makeText(this,"登入成功",Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(this,"登入失敗",Toast.LENGTH_SHORT).show();
            }
        }else {
            Toast.makeText(this,"登入失敗",Toast.LENGTH_SHORT).show();
        }
    }
           

MVP執行個體

代碼結構

Android 架構 MVC MVP MVVM,這一波你應該了然于心

1.Model層

實體類bean,同MVC中的User類,就不貼代碼浪費大家時間了。

Model層所要執行的業務邏輯

/**
  * 功能:接口,表示Model層所要執行的業務邏輯
  */
public interface LoginModel {
    //User實體類;OnLoginFinishedListener presenter業務邏輯的傳回結果
    void login(User user, OnLoginFinishedListener listener);
}
           

實作類(實作LoginModel接口)

/**
  * 功能:實作Model層邏輯
  */
public class LoginModelImpl implements LoginModel {
    //第4步:驗證帳号密碼
    @Override
    public void login(User user, OnLoginFinishedListener listener) {
        if(user.getName().isEmpty()||!user.getName().equals("scc001")){
            //第5步:Model層裡面回調Presenter層listener
            listener.onUserNameError();
        }else if(user.getPassword().isEmpty()||!user.getPassword().equals("111111")){
            //第5步:Model層裡面回調Presenter層listener
            listener.onPasswordError();
        }else {
            //第5步:Model層裡面回調Presenter層listener
            listener.onSuccess();
        }
    }
}
           

2.Presenter層

當Model層得到請求的結果,回調Presenter層,讓Presenter層調用View層的接口方法。

/**
  * 功能:當Model層得到請求的結果,回調Presenter層,讓Presenter層調用View層的接口方法。
  */
public interface OnLoginFinishedListener {
    void onUserNameError();
 
    void onPasswordError();
 
    void onSuccess();
}
           

完成登入的驗證,以及銷毀目前View。

/**
  * 功能:登入的Presenter的接口,實作類為LoginPresenterImpl,
  * 完成登入的驗證,以及銷毀目前View。
  */
public interface LoginPresenter {
    //完成登入的驗證
    void verifyData(User user);
    //銷毀目前View
    void onDestroy();
}
           

Presenter實作類,引入 LoginModel(model)和LoginView(view)的引用

/**
  * 功能:實作類,引入 LoginModel(model)和LoginView(view)的引用
  */
public class LoginPresenterImpl implements OnLoginFinishedListener, LoginPresenter {
    //View層接口
    private LoginView loginView;
    //Model層接口
    private LoginModel loginModel;
 
    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();
    }
    //第6步:通過OnLoginFinishedListener驗證結果回傳到Presenter層
    @Override
    public void onUserNameError() {
        if (loginView != null) {
            //第7步:通過loginView回傳到View層
            loginView.setUserNameError();
            loginView.hideProgress();
        }
 
    }
    //第6步:通過OnLoginFinishedListener驗證結果回傳到Presenter層
    @Override
    public void onPasswordError() {
        if (loginView != null) {
            //第7步:通過loginView回傳到View層
            loginView.setPasswordError();
            loginView.hideProgress();
        }
    }
    //第6步:通過OnLoginFinishedListener驗證結果回傳到Presenter層
    @Override
    public void onSuccess() {
        if (loginView != null) {
            //第7步:通過loginView回傳到View層
            loginView.success();
            loginView.hideProgress();
        }
    }
 
 
    @Override
    public void verifyData(User user) {
        if (loginView != null) {
            loginView.showProgress();
        }
        //第3步:調用model層LoginModel接口的login()方法
        loginModel.login(user,this);
    }
 
    @Override
    public void onDestroy() {
        loginView = null;
    }
}
           

3.View層

布局檔案同MVC中的View層,就不貼代碼浪費大家時間了。

Presenter與View互動是通過接口。

/**
  * 功能:Presenter與View互動是通過接口。
  * 接口中方法的定義是根據Activity使用者互動需要展示的控件确定的。
  */
public interface LoginView {
    //login是個耗時操作,加載中(一般用ProgressBar)
    void showProgress();
    //加載完成
    void hideProgress();
    //login賬号失敗給出提示
    void setUserNameError();
    //login密碼失敗給出提示
    void setPasswordError();
    //login成功
    void success();
}
           

MVPLoginActivity

/**
  * 功能:需要實作LoginView接口。
  */
public class MVPLoginActivity extends AppCompatActivity implements LoginView {
    LoginPresenterImpl loginPresenterImpl;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        //建立一個Presenter對象
        loginPresenterImpl = new LoginPresenterImpl(MVPLoginActivity.this);
        //第1步:使用者點選登入
        mvpBinding.mvpLogin.btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                User user = new User();
                user.setName(mvpBinding.mvpLogin.etAccount.getText().toString());
                user.setPassword(mvpBinding.mvpLogin.etPassword.getText().toString());
                //第2步:調用Presenter接口中的驗證方法
                loginPresenterImpl.verifyData(user);
            }
        });
    }
 
    @Override
    public void showProgress() {
        //加載中
    }
 
    @Override
    public void hideProgress() {
        //加載完成
    }
 
    @Override
    public void setUserNameError() {
        //第7步:通過loginView回傳到View層
        //賬号錯誤
        Toast.makeText(this,"登入失敗",Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void setPasswordError() {
        //第7步:通過loginView回傳到View層
        //密碼錯誤
        Toast.makeText(this,"登入失敗",Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void success() {
        //第7步:通過loginView回傳到View層
        Toast.makeText(this,"登入成功",Toast.LENGTH_SHORT).show();
        //登入成功
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenterImpl.onDestroy();
    }
}
           

MVVM執行個體

1.Model層

實體類bean,同MVC中的User類,就不貼代碼浪費大家時間了。

2.ViewModel層

ViewModel類,繼承自ViewModel

public class LoginViewModel extends ViewModel {
    public ViewDataBinding binding;
    public LoginViewModel(ViewDataBinding binding){
        this.binding = binding;
    }
    public void getUser(String userName, String password, Callback callback) {
        //邏輯處理
        User user = new User();
        user.setPassword("111111");
        if(userName.isEmpty()||!userName.equals("scc001")){
            user.setName("scc005");
        }else if(password.isEmpty()||!password.equals("111111")){
            user.setName("scc004");
        }else {
            user.setName("scc003");
        }
        callback.onCallBack(user);
    }
}
ViewModel與View互動

/**
  * 功能:ViewModel與View互動。
  */
public interface Callback<T> {
    void onCallBack(T t);
}
           

3.View層

先看布局檔案,布局檔案使用了DataBinding。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <!--為引入的類從新起一個變量名,友善下面使用-->
        <variable
            name="user"
            type="com.scc.architecture.mvvm.model.User" />
    </data>
    <!--删減版-->
    <LinearLayout
        ...>
        <LinearLayout
            ...>
            <EditText
                android:id="@+id/et_account"
                ...
                android:text="@={user.name}" />
        </LinearLayout>
 
        <LinearLayout
            ...>
            <EditText
                android:id="@+id/et_password"
                ...
                android:text="@={user.password}" />
        </LinearLayout>
 
        <Button
            android:id="@+id/btn_login"
            .../>
    </LinearLayout>
</layout>
           

本來Button點選事件也想用databinding去做,後來覺得這個是MVP模式就忽略了這個知識點,感興趣的可以自己搗鼓一下,databinding還是挺好玩的。

MVVMLoginActivity

public class MVVMLoginActivity extends AppCompatActivity {
    private LoginViewModel loginVM;
    ActivityMvvmBinding mvvmBinding;
    private EditText et_account,et_password;
    private Button btn_login,btn_back;
    private TextView tv_title;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mvvmBinding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
        et_account =findViewById(R.id.et_account);
        et_password =findViewById(R.id.et_password);
        btn_login = findViewById(R.id.btn_login);
        tv_title = findViewById(R.id.tv_title);
        tv_title.setText("MVVM");
 
        loginVM = new LoginViewModel(mvvmBinding);
        User user = new User( "scc001", "111111");
        mvvmBinding.setUser(user);//設定et_account:scc001|et_password:111111
        //第1步:使用者點選登入
        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login(et_account.getText().toString(),et_password.getText().toString());
            }
        });
    }
    private void login(String name,String password) {
        loginVM.getUser(name,password, new Callback<User>() {
            @Override
            public void onCallBack(User user) {
                mvvmBinding.setUser(user);//同步設定控件
            }
        });
    }
}
           

寫到這裡MVC、MCP、MVVM和執行個體基本寫完了,但是感覺自己了解的不是很好,有大佬能指點就更好了。最後,希望對你有借鑒意義。

Android知識點:

Android開發核心知識點筆記

Android Framework知識點筆記