Card image cap

Table of Contents:



Prefer video over reading? watch the video


Introduction


Personally, I think MVVM is the best way to structure code (in most situations).

The thing I like most about it is the compartmentalization of the various components in a project.
  • The UI components are kept away from the business logic
  • The business logic is kept away from the database operations
  • It's easy to read (because everything has specific places to live)
  • And if done correctly, you have a lot less to worry about when it comes to lifecycle events (ex: screen rotations)



Dependencies


build.gradle app file.
def version= '1.1.1'
implementation "android.arch.lifecycle:extensions:$version"
annotationProcessor "android.arch.lifecycle:compiler:$version"



Repositories


Repository classes handle data operations. Creating a repository is a clean way to provide clear distinctions between how an application retrieves data, and how it uses/displays that data.

Repositories know where to get the data from (ex: what REST API to query, what internal SQLite database to query, etc...) and what API calls to make when data is updated. You can consider repositories to be mediators between different data sources, such as persistent models, web services, and caches.


I know diagrams like this can seem confusing, so let me outline the most important concept:

Notice that each component depends only on the component one level below it. This is a very important concept to understand when trying to optimize your code structure. The repository is acting as a hub for incoming and outgoing data. The ViewModel is monitoring the repository for changes in the data sets, and then updates the UI accordingly.


View Models


With respect to Android development, many concepts and naming conventions can be very misleading. Take the HandlerThread class for example. If you ask me, HandlerThread is not a good name for this class. The thing that makes it different from the standard Thread class isn't the fact that it has a Handler. It's actually the built-in Looper mechanism. This is just one of the many examples of confusing naming conventions in the Android SDK.

On the other hand, the ViewModel class is described perfectly by it's name. It's a model, for a view. The view can be a fragment or an activity. And the model can be anything. It's a model for the data you want to display in the view.

A ViewModel can tell other components to retrieve data (for example a repository), and it can handle incoming requests from a user to modify the data (for example a user adding a new element to a RecycerView full of list-items).

Like a repository, a ViewModel is another form of mediation. It separates the act of retrieving or updating data sets (represented by the repository), and the current state of the UI. A ViewModel doesn't know about the lifecycle of an activity or fragment. If a configuration change occurs, it doesn't care. It's only concern is holding a accurate reference to the data for the view. If the user was to close the app and reopen it several hours later, they would see exactly the same thing as when they closed it.


Preparing the ViewModel


Now that you have an idea of the overall structure of this design pattern, let's talk about how the different components communicate. How does a view (an activity or fragment) display the data represented inside the ViewModel?

I think this is a difficult concept to understand conceptually, so I'll use an example.

Suppose you were constructing a ViewModel for an activity that displays a user profile. The user profile is simple, it has a profile image and 2 TextView's describing the users name and occupation.

First you'd want to build a User class to model the User object.

User.java
public class User{
   private String imageUrl;
   private String name;
   private String occupation;
   
   // getters and setters
   // ...
}

Next is the ViewModel. The goal here is to display the data contained in the User model on the screen. To do that you create a new class that extends ViewModel. Inside the ViewModel is where you put the data. But you can't just stick the User model inside the ViewModel class, it needs to live inside a special container. One type of special container you can use is a LiveData object.

A LiveData object is an observable data-holder class. It's responsible for holding the data that's displayed in the view. But it's not just any old data-holder, the data is observable. Meaning that the data is actively being watched for changes. If a change occurs, the view is updated automatically.

Also notice the MutableLiveData object. MutableLiveData is a subclass of LiveData. Here's a diagram if you're confused:



First, notice that I'm exending by ViewModel. There's two options, AndroidViewModel and ViewModel. You want to use AndroidViewModel if you need the application context inside the ViewModel.

You need to use a MutableLiveData object if you want to allow the LiveData object to be changed. LiveData can not be changed by itself. That is, the setValue method is not public for LiveData. But it is public for MutableLiveData. I'll talk more about this below when I make a change to the users name.

MainActivityViewModel.java
public class MainActivityViewModel extends ViewModel{
 
    private MutableLiveData<User> user;
 
    public LiveData<User> getUser() {
        return user;
    }
}

Now if any changes are made to the LiveData object (which is representing a User object), the view will be updated with the correct data automatically. Also note that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (the owner being the activity or fragment it exists in). The new instance of the owner will just re-connected to the existing ViewModel. So if the user locks the phone screen and comes back several hours later they're still going to see the exact same thing.


Communicating Between Activity and ViewModel


The next step is associating the ViewModel with the activity or fragment that it's designed for.

Start by creating a global ViewModel object. You want to create a global object so the user can make changes to the ViewModel easily. For example if they wanted to change their name. I'll talk more about that in the section below named "Communicating with a Repository".

MainActivity.java
public class MainActivity extends AppCompatActivity{
 
private MainActivityViewModel mMainActivityViewModel;
private ImageView mProfileImage;
private Textview mName;
private TextView mOccupation;
 
// ...
}

Now comes the actual associating of the ViewModel with the Activity or Fragment:

MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProfileImage = findViewById(R.id.image);
        mName= findViewById(R.id.name);
        mOccupation= findViewById(R.id.occupation);
        mMainActivityViewModel = ViewModelProviders.of(this)
          .get(MainActivityViewModel.class);
}

And finally, call the "getUser" method on the MainActivityViewModel and observe the changes:

MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mProfileImage = findViewById(R.id.image);
    mName= findViewById(R.id.name);
    mOccupation= findViewById(R.id.occupation);
    mMainActivityViewModel = ViewModelProviders.of(this)
      .get(MainActivityViewModel.class);
    mMainActivityViewModel.getUser()
      .observe(this, new Observer<User>(){
          @Override
          public void onChanged(User user) {
          // Set profile image
          mName.setText(user.getName());
          mOccupation.setText(user.getOccupation());
      }
    });



Retrieving Data in the Repository


The repository is designed to supply up-to-date data to the ViewModel. But how does the repository actually aquire that data? In the case of a REST API, the repository would need to make a request to a server using something like HTTP. In the case of a local SQLite database, the repository would simply query the database in the phone.

Here's an example of what a repository might look like querying a server for user data given a specific userId. Keep in mind this is just an example. In your actual project you'd want to create an instance/singleton object for accessing a web service (Like an application-wide singleton instance of Retrofit for example). If a different instance was used inside each and every ViewModel, your application would almost certainly start to struggle.

UserRepository.java
public class UserRepository {
 
private static UserRepository instance;
 
public static getInstance(){
     if(instance == null){
        instance = new UserRepository();
     }
     return instance;
 }
 
private WebService mFakeWebService =
   WebService.getInstance();

public LiveData<User> getUser(String userId) {
    
    final MutableLiveData<User> userData =
        new MutableLiveData<>();
    userData.setValue(
        mFakeWebService.getUser(userId)
    );
    return userData;
}

Pay close attention to how I'm using the MutableLiveData object here. In the getUser(String userId) method I'm instantiating a new MutableLiveData object, setting the value using the setValue(mFakeWebService.getUser(userId)) method, then returning the MutableLiveData object. Remember, I'm doing this because a LiveData object does not have a public setValue method, so I need to set the data using a MutableLiveData object.


Communicating with a Repository


The next step is telling the repository to retrieve the data. Once it's retrieved, the LiveData object will get updated, resulting in the onChange method being called in the Observer. Recall onChange from MainActivity:

MainActivity.java
        mMainActivityViewModel.getUser()
          .observe(this, new Observer<User>(){
              @Override
              public void onChanged(User user) {
              // ... setter methods
              }
        });


To initiate the query in the repository we need to create a new method in the ViewModel class. Here's an example of retrieving data from the repository in the ViewModel. Notice I'm using an instance of UserRepository to call the getUser(String userId) method.

MainActivityViewModel.java
public class MainActivityViewModel extends ViewModel{
 
    private MutableLiveData<User> user;
    private UserRepository userRepo;
 
    public void queryRepo(String userId) {
        userRepo = UserRepository.getInstance();
        user = userRepo.getUser(userId);
    }

 
    public LiveData<User> getUser() {
        return user;
    }
}

Now call the queryRepo(String userId) method in MainActivity:

MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    mMainActivityViewModel = ViewModelProviders.of(this)
      .get(MainActivityViewModel.class);
    mMainActivityViewModel.queryRepo("someUserId");
    mMainActivityViewModel.getUser()
      .observe(this, new Observer<User>(){
        @Override
        public void onChanged(User user) {
            // ...
        }
    });
}

That will take care of the query. At this point you'll see data in the Activity.


Updating LiveData Values


Suppose the user wanted to update the name field in their profile. This is what you'd need to do. First we need a method in the ViewModel for updating the user object.

MainActivityViewModel.java
public class MainActivityViewModel extends ViewModel{
 
    private MutableLiveData<User> user;
    private UserRepository userRepo;
 
    // ...
    public LiveData<User> getUser() {
        return user;
    }
 
    public void updateUser(User updatedUser) {
        user.postValue(updatedUser);
    }

}

Then in MainActivity you set the new value after capturing the new input.

MainActivity.java
updatedUser.set(newName);
mMainActivityViewModel.updateUser(updatedUser);



Final Thoughts


I know this probably seems like a complicated process, but once you get the hang of it, it's really quite straight forward.

MVVM is my favorite way to structure code. I absolutely love the way everything is compartmentalized. It's easy to read, and if done properly, yields an excellent user experience. The only reason I don't use it more often in my videos is there's not a lot of beginners that understand how it works. And when teaching, it's important to eliminate as many points of confusion as possible.

I know the power of MVVM probably isn't that obvious with this example. In January 2019 I'll publish a REST API integration using MVVM. And if there's time, I'll create a data cache as well. then you'll see the true power of MVVM :).


Staying in the Loop


If you want to stay in the loop and get an email when I write new blog posts, Follow me on Instagram or join the CodingWithMitch community on my website. It only takes about 30 seconds to register.





Authors


Mitch Tabian

codingwithmitch.com

Software Dev


Create an Account



Have an account? Log In

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

Comments