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)。
工作原理
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CZ3YDZhJjNhhTM0ITOwkDN5IWMidDZxMjMxcTNmhDN48CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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間的互動和業務邏輯。
工作原理
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更改時,實時重新整理對方。
工作原理
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。
如何選取架構
本來是要每個模式寫一個适用場景,最後想想每個人都有自己的了解,别被他人束縛了。
一句話:适合自己的才是最好的!
執行個體
就這麼一個界面咱通過MVC、MVP、MVVM分别搭建一下。
MVC執行個體
代碼結構
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執行個體
代碼結構
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知識點筆記