前言
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),
作業原理

-
1.View接受用戶的互動請求,
-
2.View將請求轉交給Controller,
-
3.Controller操作Model進行資料更新,
-
4.資料更新之后,Model通知View資料變化,
-
5.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間的互動和業務邏輯,
作業原理

-
1.View 接收用戶互動請求
-
2.View 將請求轉交給 Presenter(V呼叫P介面)
-
3.Presenter 操作Model進行資料更新(P呼叫M介面)
-
4.Model 通知Presenter資料發生變化(M呼叫P介面)
-
5.Presenter 更新View資料(P執行介面,V相應回呼)
MVP的優點
-
1.復雜的邏輯處理放在Presenter進行處理,減少了Activity的臃腫,
-
2.解耦,Model層與View層完全分離,修改V層不會影響M層,降低了耦合性,
-
3.可以將一個Presenter用于多個視圖,而不需要改變Presenter的邏輯,
-
4.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更改時,實時重繪對方,
作業原理

-
1.View 接收用戶互動請求
-
2.View 將請求轉交給ViewModel
-
3.ViewModel 操作Model資料更新
-
4.Model 更新完資料,通知ViewModel資料發生變化
-
5.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"
.../>
<Button
android:id="@+id/btn_back"
.../>
</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和實體基本寫完了,但是感覺自己理解的不是很好,有大佬能指點就更好了,最后,希望對你有借鑒意義,
實體傳送門
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/294676.html
標籤:其他
