Google announced the new architectural pattern Model-View-View-Model (or MVVM), which has gained a lot of popularity for increased simplicity. The Open Event Organizer Android App is currently (at the time of writing this blog) using Model-View-Presenter (MVP) architecture for the most part. Only the Login module had been converted to MVVM, but it wasn’t using any Shared View Model.
Let’s look at the problem at hand:
For the Login fragment, there’s an email field for user’s email in every fragment of the auth module. But there’s the use case where users can bounce around different fragment screens in order to perform different operations like login, signup, forgot password and enter new password.
A good User Experience is at the core of every successful product. Users don’t like to spend time doing trivial things like registration and they hate to retype emails again and again.
To solve the problem, the approach that I first used was, using interfaces to tell each and every component when the fragment opens and send the email text through the interface. I spent quite some time implementing the interfaces right and then opened a PR. It was around 200 lines addition at first, and I also received 2-3 requested changes from my project mentor.
Defining the interface:
The interface EmailIdTransfer was defined in the LoginFragment as follows:
public class LoginFragment extends BaseFragment implements LoginView, EmailIdTransfer { |
Using the interface:
getPresenter().getEmailId().setEmail( ((LoginFragment.EmailIdTransfer) getContext()).getEmail() |
Imagine setting up the EmailIdTransfer interface for each Fragment, and handling their updation and reception.
As you can see, this is a bad solution for something as trivial as the problem at hand! The better solution which stuck me (from Google I/O) afterwards was to use ViewModels. This obviously wasn’t MVP, but since we had to eventually move to MVVM, I dug a little more about it and found out that there’s also a SharedViewModel.
SharedViewModel is basically a View Model that every view of a module shares. It is not very different from ViewModel.
Here’s how to set it up:
public class SharedViewModel extends ViewModel { private final MutableLiveData<String> email = new MutableLiveData<>(); public void setEmail(String email) { this.email.setValue(email); } public LiveData<String> getEmail() { return email; } } |
The MVVM architecture uses LiveData instead of Observables.
Android LiveData is a variant of the original observer pattern, with the addition of active/inactive transitions. As such, it is very restrictive in its scope. RxJava provides operators that are much more generalized.
Now to use this SharedViewModel in each fragment, we need:
sharedViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
sharedViewModel.getEmail().observe(this, email -> binding.getForgotEmail().setEmail(email)); |
This is the initial setup for the the fragment.
private void openLoginPage() {
… |
This is needed to be done for every fragment which needs to set and use the common email.
This was a dive into using LiveData<>, ViewModel and how to use a SharedViewModel.
This is how the result looks like:
References:
- Android Developer Guide on View Models
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing - Pull Request #982:
https://github.com/fossasia/open-event-orga-app/pull/982