Android MVVM 设计模式

Posted

技术标签:

【中文标题】Android MVVM 设计模式【英文标题】:Android MVVM Design Pattern 【发布时间】:2014-08-31 23:10:24 【问题描述】:

我在最近发布的“android 最佳实践”一书中读到,用于 android 编程的一个很好的设计模式是 MVVM。在我的最新项目中亲自尝试过它似乎确实有助于将代码分成更易于管理的部分。

View 只处理视图项的创建和 ViewModel 的接口。 ViewModel 实现对视图的接口和handlss 操作以及与模型的交互。示例代码如下:

型号

 public class MyModel
    public String myString;
    public MyModel(String myString)
       this.myString = myString;
    

查看

public class MyActivity

    public ViewManager delegate;

    public interface ViewManager
        void registerTextView(TextView tvText);
    

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        delegate = new ViewController(this);
        TextView tvText = (TextView) view.findViewById(R.id.tvText);
        delegate.registerTextView(tvText);
    

视图模型

 public class ViewController implements MyActivity.ViewManager
     Context activity;
     TextView tvText;
     MyModel myModel;

     public ViewController(Context app_context)
        activity = app_context;
        myModel = new MyModel("Hello World");
     

    @Override
    public registerTextView(TextView tvText)
        this.tvText = tvText;
        tvText.setText(myModel.myString);           
    
 

但是,我在其他任何地方都没有在网上看到过这种方法,也无法找到很多信息来支持它是一个很好的 android 设计模式。我也有几个问题,例如:

您应该为每个片段或只是为活动创建一个单独的 ViewModel 吗?

这种方法在配置更改和 Activity 重新创建方面是否表现良好,但会产生另一个类的额外开销? 您可以将上下文投射到您的活动中以启用片段管理器吗?

随着代码变得越来越复杂,这将如何扩展?

在我开始将所有项目转换为 MVVM 之前,有没有人有使用这种设计模式的经验,或者任何人都可以指出一些好的学习材料的方向???

【问题讨论】:

你可以用你的代码做任何你喜欢的事情,没有标准。我个人不使用这种模式;除非我出于某种原因需要它,否则我觉得它只是增加了不必要的抽象。 【参考方案1】:

我会尽量发表我的意见。我认为您提供的示例代码没有遵循应用 MVVM(或表示模型。MVVM 起源于表示模型)模式的核心价值。该模式的主要动机之一是使 ViewModel(或 Presentaion Model)成为纯 POJO,以便 ViewModel 允许最大可测试性。我没有读过这本书,但我建议你阅读 Martin Fowler 关于模式的原始文章。我创建了一些示例来演示如何在 Android 开发中应用该模式。如果你有兴趣,可以看看这里 - Album Sample,这是 Martin Fowler 原始专辑示例的 android 翻译,以及 AndroidMVVM,一个最小的演示应用程序。

应用该模式的一种方式是:View(Activity 或 Fragment+layout)、ViewModel、Model(业务模型:持久层、网络等)。通过这种方法,为了回答您的问题,我认为一个片段映射到一个 ViewModel。

模式是为了改进设计。如果应用得当,它将降低复杂性,而不是相反。希望这会有所帮助。

【讨论】:

【参考方案2】:

Android MVVM 设计模式

数据绑定库提供了灵活性和广泛的兼容性——它是一个支持库,因此您可以将它与所有 Android 平台版本(回溯到 Android 2.1)一起使用

构建环境

android 
    ....
    dataBinding 
        enabled = true
    

您可以按照这个link 一步一步地在您的android 项目中应用数据绑定。

高级指南转到开发者页面Link

【讨论】:

【参考方案3】:

我一直在开发一个用于以 MVVM 模式构建 Android 应用程序的库。您应该在那里找到示例。

https://github.com/manas-chaudhari/android-mvvm

核心理念:

每个 XML/View 都必须有一个 ViewModel,尽管多个 XML 可以共享一个 ViewModel 每个 ViewModel 都应该有一个模型。多个 ViewModel 可以共享一个模型 使用数据绑定链接 ViewModel View

架构相关博文:https://manaschaudhari.com/blog/2016/08/19/rxjava-meets-data-binding-part-3

【讨论】:

MVVM 演示应用程序...github.com/prakashshuklahub/Simple-Notes-Kotlin-App【参考方案4】:

我对可用代码进行了一些重构,以获得一些要点,例如如何绑定。


1。用户模型类
package com.example.mvvm.model;

public class User 
    private String email;
    private String myPassword;

    public User(String email, String password) 
        this.email = email;
        this.myPassword = password;
    

    public void setEmail(String email) 
        this.email = email;
    
    public String getEmail() 
        return email;
    

    public void setMyPassword(String myPassword) 
        this.myPassword = myPassword;
    
    public String getMyPassword() 
        return myPassword;
    

2。 LoginViewModel 类
package com.example.mvvm.viewmodels;
import android.text.TextUtils;
import android.util.Patterns;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;

import com.example.mvvm.BR;
import com.example.mvvm.model.User;

public class LoginViewModel extends BaseObservable 

    @Bindable
    private User user = new User("","");

    private String successMessage = "Login was successful";
    private String errorMessage = "Email or Password not valid";

    @Bindable
    private String toastMessage = null;

    public String getToastMessage() 
        return toastMessage;
    
    private void setToastMessage(String toastMessage) 

        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
        notifyPropertyChanged(BR.user);
    

    public void setUserEmail(String email) 
        user.setEmail(email);
        notifyPropertyChanged(BR.user);
    

    public void setUserPassword(String password) 
        user.setMyPassword(password);
        notifyPropertyChanged(BR.user);
    

    @Bindable
    public User getUser() 
        return user;
    

    public void onLoginClicked() 
        if (isInputDataValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    

    public boolean isInputDataValid() 
        return !TextUtils.isEmpty(getUser().getEmail()) &&
           Patterns.EMAIL_ADDRESS.matcher(getUser().getEmail()).matches() &&
                getUser().getMyPassword().length() > 5;
    

3。 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/tools">


    <data>
        <variable
            name="viewModel"
            type="com.example.mvvm.viewmodels.LoginViewModel" />
    </data>


    <ScrollView
        android:layout_
        android:layout_>

        <LinearLayout
            android:layout_
            android:layout_
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <EditText
                android:id="@+id/inEmail"
                android:layout_
                android:layout_
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:padding="8dp"
                android:text="@=viewModel.user.email" />


            <EditText
                android:id="@+id/inPassword"
                android:layout_
                android:layout_
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="8dp"
                android:text="@=viewModel.user.myPassword" />


            <Button
                android:layout_
                android:layout_
                android:layout_marginTop="8dp"
                android:onClick="@()-> viewModel.onLoginClicked()"
                android:text="LOGIN"
                bind:user="@viewModel.user" />


        </LinearLayout>

    </ScrollView>

</layout>
4。 MainActivity 视图类
package com.example.mvvm.views;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.BindingAdapter;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.example.mvvm.R;
import com.example.mvvm.databinding.ActivityMainBinding;
import com.example.mvvm.model.User;
import com.example.mvvm.viewmodels.LoginViewModel;

import java.sql.Time;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.executePendingBindings();
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.inEmail.setText("example@gmail.com");
        activityMainBinding.inPassword.setText("password");

    

    @BindingAdapter("user")
    public static void runtest(View view, User user) 
        if (user.getEmail() != null)
            Toast.makeText(view.getContext(), user.getEmail(), Toast.LENGTH_SHORT).show();
    

【讨论】:

以上是关于Android MVVM 设计模式的主要内容,如果未能解决你的问题,请参考以下文章

Android MVVM 设计模式

在 android 中使用 MVVM 设计模式时出错找不到符号类 ViewModel

Android MVVM模式

Android进阶之MVVM+DataBinding框架模式(更新中)

Android框架模式——MVVM

浅谈Android开发中的MVVM模式