In diesem Tutorial werden wir das Android MVVM-Architekturmuster in unserer Android-Anwendung diskutieren und implementieren. Wir haben zuvor das Android MVP-Muster besprochen.
Warum brauchen wir diese Muster? Das Hinzufügen aller Funktionen in einer einzigen Aktivität oder einem Fragment würde zu Problemen beim Testen und Refactoring des Codes führen. Daher wird die Verwendung von Code-Trennung und sauberer Architektur empfohlen.
Android MVVM
MVVM steht für Modell, Ansicht, ViewModel.
- Modell: Hier werden die Daten der Anwendung gehalten. Es kann nicht direkt mit der Ansicht kommunizieren. Im Allgemeinen wird empfohlen, die Daten über Observables an das ViewModel weiterzugeben.
- Ansicht: Sie repräsentiert die Benutzeroberfläche der Anwendung ohne Anwendungslogik. Sie beobachtet das ViewModel.
- ViewModel: Es fungiert als Verbindung zwischen dem Modell und der Ansicht. Es ist dafür verantwortlich, die Daten aus dem Modell zu transformieren. Es stellt Datenströme für die Ansicht bereit. Es verwendet auch Hooks oder Callbacks, um die Ansicht zu aktualisieren. Es wird die Daten vom Modell anfordern.
Die folgende Struktur veranschaulicht das Kernkonzept des MVVM-Musters. Wie unterscheidet sich dies von MVP?
- Das ViewModel ersetzt den Presenter in der mittleren Schicht.
- Der Presenter hält Verweise auf die Ansicht. Das ViewModel nicht.
- Der Presenter aktualisiert die Ansicht auf klassische Weise (durch Auslösen von Methoden).
- Das ViewModel sendet Datenströme.
- Der Presenter und die Ansicht befinden sich in einer 1-zu-1-Beziehung.
- Die Ansicht und das ViewModel befinden sich in einer 1-zu-vielen-Beziehung.
- Das ViewModel weiß nicht, dass die Ansicht ihm zuhört.
Es gibt zwei Möglichkeiten, MVVM in Android zu implementieren:
- Datenbindung
- RXJava
In diesem Tutorial verwenden wir nur die Datenbindung. Die Data Binding-Bibliothek wurde von Google eingeführt, um Daten direkt im XML-Layout zu binden. Für weitere Informationen zur Datenbindung siehe dieses Tutorial. Wir erstellen eine einfache Beispielanwendung für eine Anmeldeseite, die nach Benutzereingaben fragt. Wir werden sehen, wie das ViewModel die Ansicht benachrichtigt, wann eine Toast-Nachricht angezeigt werden soll, ohne eine Referenz zur Ansicht zu behalten.
Wie ist es möglich, eine Klasse zu benachrichtigen, ohne eine Referenz davon zu haben? Es gibt drei verschiedene Möglichkeiten, dies zu tun:
- Verwendung der Two Way Data Binding
- Verwendung von LiveData
- Verwendung von RxJava
Zwei-Wege-Datenbindung
Zwei-Wege-Datenbindung ist eine Technik, bei der Ihre Objekte an Ihre XML-Layouts gebunden werden, sodass das Objekt und das Layout Daten miteinander austauschen können. In unserem Fall kann das ViewModel Daten an das Layout senden und auch Änderungen beobachten. Dafür benötigen wir einen BindingAdapter
und ein benutzerdefiniertes Attribut, das in der XML definiert ist. Der Binding Adapter würde Änderungen an der Attributseigenschaft überwachen. Wir werden mehr über die Zwei-Wege-Datenbindung anhand unseres Beispiels unten erfahren.
Android MVVM Beispiel Projektstruktur
Hinzufügen der Data Binding-Bibliothek
Fügen Sie den folgenden Code zur build.gradle-Datei Ihrer App hinzu:
android {
dataBinding {
enabled = true
}
}
Dies aktiviert die Data Binding in Ihrer Anwendung.
Hinzufügen der Abhängigkeiten
Fügen Sie die folgenden Abhängigkeiten in Ihre build.gradle
-Datei hinzu:
implementation 'android.arch.lifecycle:extensions:1.1.0'
Modell
Das Modell würde die E-Mail und das Passwort des Benutzers enthalten. Die folgende Klasse User.java tut dies:
package com.journaldev.androidmvvmbasics.model;
public class User {
private String email;
private String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}
Zwei-Wege-Datenbindung ermöglicht es uns, Objekte in den XML-Layouts so zu binden, dass das Objekt Daten an das Layout senden und umgekehrt kann. Die Syntax für die Zwei-Wege-Datenbindung ist @={variable}
Layout
Der Code für die activity_main.xml ist unten angegeben:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:bind="https://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:orientation="vertical">
<EditText
android:id="@+id/inEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress"
android:padding="8dp"
android:text="@={viewModel.userEmail}" />
<EditText
android:id="@+id/inPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
android:padding="8dp"
android:text="@={viewModel.userPassword}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:onClick="@{()-> viewModel.onLoginClicked()}"
android:text="LOGIN"
bind:toastMessage="@{viewModel.toastMessage}" />
</LinearLayout>
</ScrollView>
</layout>
Datenbindung erfordert, dass wir das Layout-Tag oben setzen. Hier bindet unser ViewModel die Daten an die Ansicht. ()-> viewModel.onLoginClicked()
ruft den Button-Klick-Listener-Lambda auf, der in unserem ViewModel definiert ist. Das EditText aktualisiert die Werte im Modell (über das ViewModel). bind:toastMessage="@{viewModel.toastMessage}"
ist ein benutzerdefiniertes Attribut, das wir für die Zwei-Wege-Datenbindung erstellt haben. Basierend auf Änderungen in der toastMessage im ViewModel würde der BindingAdapter in der Ansicht ausgelöst werden.
ViewModel
Der Code für das LoginViewModel.java ist unten angegeben:
package com.journaldev.androidmvvmbasics.viewmodels;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.text.TextUtils;
import android.util.Patterns;
import com.android.databinding.library.baseAdapters.BR;
import com.journaldev.androidmvvmbasics.model.User;
public class LoginViewModel extends BaseObservable {
private User 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);
}
public void setUserEmail(String email) {
user.setEmail(email);
notifyPropertyChanged(BR.userEmail);
}
@Bindable
public String getUserEmail() {
return user.getEmail();
}
@Bindable
public String getUserPassword() {
return user.getPassword();
}
public void setUserPassword(String password) {
user.setPassword(password);
notifyPropertyChanged(BR.userPassword);
}
public LoginViewModel() {
user = new User("","");
}
public void onLoginClicked() {
if (isInputDataValid())
setToastMessage(successMessage);
else
setToastMessage(errorMessage);
}
public boolean isInputDataValid() {
return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches() && getUserPassword().length() > 5;
}
}
Die Methoden, die im Layout aufgerufen wurden, sind im obigen Code mit der gleichen Signatur implementiert. Wenn das XML-Gegenstück der Methode nicht existiert, müssen wir das Attribut in app:
ändern. Die obige Klasse kann auch ViewModel erweitern. Aber wir brauchen BaseObservable, da es die Daten in Streams umwandelt und benachrichtigt, wenn sich die toastMessage
-Eigenschaft ändert. Wir müssen den Getter und Setter für das toastMessage-Custom-Attribut, das im XML definiert ist, festlegen. Im Setter benachrichtigen wir den Beobachter (der die Ansicht in unserer Anwendung sein wird), dass sich die Daten geändert haben. Die Ansicht (unsere Aktivität) kann die entsprechende Aktion definieren.
Die BR-Klasse wird beim Neubauen des Projekts automatisch aus der Datenbindung generiert.
Der Code für die Klasse MainActivity.java
ist unten aufgeführt:
package com.journaldev.androidmvvmbasics.views;
import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.journaldev.androidmvvmbasics.R;
import com.journaldev.androidmvvmbasics.databinding.ActivityMainBinding;
import com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel;
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();
}
@BindingAdapter({"toastMessage"})
public static void runMe(View view, String message) {
if (message != null)
Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
}
}
Dank DataBinding wird die Klasse ActivityMainBinding
automatisch aus dem Layout generiert. Die @BindingAdapter
-Methode wird jedes Mal ausgelöst, wenn das toastMessage-Attribut, das auf dem Button definiert ist, geändert wird. Es muss dasselbe Attribut wie im XML und im ViewModel verwenden. In der obigen Anwendung aktualisiert das ViewModel das Modell, indem es auf Änderungen in der Ansicht hört. Außerdem kann das Modell die Ansicht über das ViewModel mithilfe von notifyPropertyChanged
aktualisieren. Die Ausgabe der obigen Anwendung in Aktion ist unten dargestellt: Damit endet dieses Tutorial zu Android MVVM mit DataBinding. Sie können das Projekt über den unten angegebenen Link herunterladen.
Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern