📜  Android中的MVVM(模型视图ViewModel)架构模式

📅  最后修改于: 2021-05-10 16:55:39             🧑  作者: Mango

开发人员始终喜欢为项目提供干净,结构化的代码。通过根据设计模式组织代码有助于软件的维护。通过了解android应用程序的所有关键逻辑部分,可以轻松添加和删除应用程序功能。此外,设计模式还确保所有代码都在单元测试中涵盖,而不会受到其他类的干扰。模型—视图— ViewModel(MVVM)是业界公认的软件体系结构模式,它克服了MVP和MVC设计模式的所有缺点。 MVVM建议将数据表示逻辑(视图或UI)与应用程序的核心业务逻辑部分分开。

MVVM的单独代码层是:

  • 模型:该层负责数据源的抽象。 Model和ViewModel一起工作以获取和保存数据。
  • 视图:该层的目的是向ViewModel通知用户的操作。该层遵守ViewModel,不包含任何类型的应用程序逻辑。
  • ViewModel:它公开了与View相关的那些数据流。而且,它作为模型与视图之间的链接而充当服务器。

Android中的MVVM(模型视图ViewModel)架构模式

MVVM模式与MVP(模型—视图—演示者)设计模式有一些相似之处,因为演示者角色是由ViewModel扮演的。但是,MVVM已通过以下方式解决了MVP模式的缺点:

  1. ViewModel不保存对View的任何引用。
  2. View和ViewModel之间存在多对一的关系。
  3. 没有触发方法来更新视图。

在项目中实现MVVM的方法

有两种方法可以在Android项目中实现MVVM设计模式:

  1. 使用Google发布的DataBinding库
  2. 使用RxJava之类的任何工具进行DataBinding。

数据绑定:

Google发布了适用于Android的数据绑定库,该库允许开发人员将XML布局中的UI组件与应用程序的数据存储库绑定。这有助于最大程度地减少与View绑定的核心应用程序逻辑的代码。此外,完成了双向数据绑定以将对象绑定到XML布局,以便对象和布局都可以相互发送数据。这一点可以通过本教程的示例直观地看到。

MVVM体系结构模式示例

这是一个单一活动User-Login android应用程序的示例,用于显示MVVM架构模式在项目上的实现。该应用程序将要求用户输入电子邮件ID和密码。根据收到的输入,ViewModel通知View显示为Toast消息 ViewModel将没有对View的引用。

以下是带有MVVM模式的User-Login android应用程序的完整分步实施。

分步实施

步骤1:创建一个新项目

  • 单击文件,然后单击新建=>新建项目。
  • 选择清空活动
  • 选择语言作为Java/科特林
  • 根据需要选择最小的SDK。

步骤2:修改String.xml文件

此文件中列出了活动中使用的所有字符串。

XML

    GfG | MVVM Architecture
    MVVM Architecture Pattern
    Enter your Email ID
    Enter your password
    Login


Java
import androidx.annotation.Nullable;
  
public class Model {
  
    @Nullable
    String email,password;
  
    // constructor to initialize
    // the variables
    public Model(String email, String password){
        this.email = email;
        this.password = password;
    }
  
    // getter and setter methods
    // for email variable
    @Nullable
    public String getEmail() {
        return email;
    }
  
    public void setEmail(@Nullable String email) {
        this.email = email;
    }
  
    // getter and setter methods
    // for password variable
    @Nullable
    public String getPassword() {
        return password;
    }
  
    public void setPassword(@Nullable String password) {
        this.password = password;
    }
  
}


XML


  
    
    
        
    
  
    
    
  
        
        
  
        
        
  
        
        
  
        
        


Java
import android.text.TextUtils;
import android.util.Patterns;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
  
public class AppViewModel extends BaseObservable {
  
    // creating object of Model class
    private Model model;
  
    // string variables for
    // toast messages
    private String successMessage = "Login successful";
    private String errorMessage = "Email or Password is not valid";
  
    @Bindable
    // string variable for
    // toast message
    private String toastMessage = null;
  
    // getter and setter methods
    // for toast message
    public String getToastMessage() {
        return toastMessage;
    }
  
    private void setToastMessage(String toastMessage) {
        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
    }
  
    // getter and setter methods
    // for email varibale
    @Bindable
    public String getUserEmail() {
        return model.getEmail();
    }
  
    public void setUserEmail(String email) {
        model.setEmail(email);
        notifyPropertyChanged(BR.userEmail);
    }
  
    // getter and setter methods
    // for password variable
    @Bindable
    public String getUserPassword() {
        return model.getPassword();
    }
  
    public void setUserPassword(String password) {
        model.setPassword(password);
        notifyPropertyChanged(BR.userPassword);
    }
  
    // constructor of ViewModel class
    public AppViewModel() {
  
        // instantiating object of
        // model class
        model = new Model("","");
    }
  
    // actions to be performed
    // when user clicks
    // the LOGIN button
    public void onButtonClicked() {
        if (isValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    }
  
    // method to keep a check
    // that variable fields must
    // not be kept empty by user
    public boolean isValid() {
        return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches()
                && getUserPassword().length() > 5;
    }
}


Java
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
  
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.BindingAdapter;
import androidx.databinding.DataBindingUtil;
  
import com.example.mvvmarchitecture.databinding.ActivityMainBinding;
  
public class MainActivity extends AppCompatActivity {
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  
        // ViewModel updates the Model
        // after observing changes in the View
  
        // model will also update the view
        // via the ViewModel
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new AppViewModel());
        activityMainBinding.executePendingBindings();
  
    }
  
    // any change in toastMessage attribute
    // defined on the Button with bind prefix
    // invokes this method
    @BindingAdapter({"toastMessage"})
    public static void runMe( View view, String message) {
        if (message != null)
            Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
    }
}


步骤3:建立Model类别

创建一个名为Model的新类,该类将保存用户输入的电子邮件ID和密码。下面是实现适当的Model类的代码。

Java

import androidx.annotation.Nullable;
  
public class Model {
  
    @Nullable
    String email,password;
  
    // constructor to initialize
    // the variables
    public Model(String email, String password){
        this.email = email;
        this.password = password;
    }
  
    // getter and setter methods
    // for email variable
    @Nullable
    public String getEmail() {
        return email;
    }
  
    public void setEmail(@Nullable String email) {
        this.email = email;
    }
  
    // getter and setter methods
    // for password variable
    @Nullable
    public String getPassword() {
        return password;
    }
  
    public void setPassword(@Nullable String password) {
        this.password = password;
    }
  
}

步骤4:使用activity_main.xml文件

打开activity_main.xml文件,并添加2 EditText以获取“电子邮件”和“密码”的输入。还需要一个登录按钮来验证用户的输入并显示适当的Toast消息。以下是用于设计适当活动布局的代码。

XML格式



  
    
    
        
    
  
    
    
  
        
        
  
        
        
  
        
        
  
        
        

步骤5:创建ViewModel类

此类将包含在应用程序布局中需要调用的所有方法。 ViewModel类将扩展BaseObservable,因为它会将数据转换为流,并在Toast消息属性更改时通知View

Java

import android.text.TextUtils;
import android.util.Patterns;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
  
public class AppViewModel extends BaseObservable {
  
    // creating object of Model class
    private Model model;
  
    // string variables for
    // toast messages
    private String successMessage = "Login successful";
    private String errorMessage = "Email or Password is not valid";
  
    @Bindable
    // string variable for
    // toast message
    private String toastMessage = null;
  
    // getter and setter methods
    // for toast message
    public String getToastMessage() {
        return toastMessage;
    }
  
    private void setToastMessage(String toastMessage) {
        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
    }
  
    // getter and setter methods
    // for email varibale
    @Bindable
    public String getUserEmail() {
        return model.getEmail();
    }
  
    public void setUserEmail(String email) {
        model.setEmail(email);
        notifyPropertyChanged(BR.userEmail);
    }
  
    // getter and setter methods
    // for password variable
    @Bindable
    public String getUserPassword() {
        return model.getPassword();
    }
  
    public void setUserPassword(String password) {
        model.setPassword(password);
        notifyPropertyChanged(BR.userPassword);
    }
  
    // constructor of ViewModel class
    public AppViewModel() {
  
        // instantiating object of
        // model class
        model = new Model("","");
    }
  
    // actions to be performed
    // when user clicks
    // the LOGIN button
    public void onButtonClicked() {
        if (isValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    }
  
    // method to keep a check
    // that variable fields must
    // not be kept empty by user
    public boolean isValid() {
        return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches()
                && getUserPassword().length() > 5;
    }
}

步骤6:在MainActivity文件中定义View的功能

View类负责更新应用程序的UI。根据ViewModel提供的Toast消息中的更改,绑定适配器将触发View层。 Toast消息的设置者将通知观察者(View)有关数据的更改。之后,View将采取适当的措施。

Java

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
  
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.BindingAdapter;
import androidx.databinding.DataBindingUtil;
  
import com.example.mvvmarchitecture.databinding.ActivityMainBinding;
  
public class MainActivity extends AppCompatActivity {
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  
        // ViewModel updates the Model
        // after observing changes in the View
  
        // model will also update the view
        // via the ViewModel
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new AppViewModel());
        activityMainBinding.executePendingBindings();
  
    }
  
    // any change in toastMessage attribute
    // defined on the Button with bind prefix
    // invokes this method
    @BindingAdapter({"toastMessage"})
    public static void runMe( View view, String message) {
        if (message != null)
            Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
    }
}

输出

MVVM体系结构的优势

  • 增强代码的可重用性。
  • 所有模块都是独立的,从而提高了每个层的可测试性。
  • 使项目文件可维护且易于更改。

MVVM体系结构的缺点

  • 这种设计模式不适用于小型项目。
  • 如果数据绑定逻辑太复杂,则应用程序调试会更加困难。
想要一个节奏更快,更具竞争性的环境来学习Android的基础知识吗?
单击此处,前往由我们的专家精心策划的指南,以使您立即做好行业准备!