MVVM on Android with the Architecture Components

One of the biggest Android news at I/O this year was the Android Architecture Components. Because my very first conference talk back in 2015 was on SQLite Database and Content Provider, I took special interests in the Architecture Components when I heard Room can help eliminate the boiler-plate code in data persistence.

MVVM

Android Architecture Components facilitates the MVVM (Model-View-ViewModel) architecture and this is the first time the Android Framework team officially provides an official guidance on an Android app architecture.

MVVM is not new, and was designed by Microsoft in 2005. Let’s see how this can be implemented on Android with the new Android Architecture Components. I put together (a very much simplified) MVVM diagram with the new components highlighted. Note you can also add another layer between ViewModel and RoomDatabase such as a data Repository class. This diagram only shows data persistence on device. Your app may also fetches data from the network.

Model-View-ViewModel with Android Architecture Components

Implementation steps

Follow these steps to implement MVVM with the new Android Architecture Components in your app:

  • Add dependencies in app module build.gradle for architecture components
  • Create a entity class, a POJO which also defines database table schema
  • Create DAO (Data Access Object), an interface annotated with “@Dao”
  • Create database, an abstract class that extends from RoomDatabase
  • Create a ViewModel class that contains LiveData, alternatively use RxJava, AsyncTask for the async operations on the SQLite database
  • Create UI (Activity & Fragment) and get an instance of ViewModel with ViewModelProviders’ get() method
  • Observe the data changes with LiveData (or other async calls), and update UI upon data changes

Update build.gradle

In the app module build.gradle file, add the following dependencies for Room, Lifecycles, LiveData and ViewModel

// Architecture Component - Room
compile "android.arch.persistence.room:runtime:1.0.0-alpha5"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha5"

// Lifecyles, LiveData and ViewModel
compile "android.arch.lifecycle:runtime:1.0.0-alpha5"
compile "android.arch.lifecycle:extensions:1.0.0-alpha5"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha5"

Model — Entity, DAO and Room

The Room persistence library has 3 main components: Entity, DAO and Database. To create the data model layer, we first create an Entity or multiple Entities, a DAO (Data access object) for each Entity, and then a class that extends from RoomDatabase.

Entity

The entity is just a POJO (plain old java object) which is also going to be the table in the database. For example, you can create a Task class and annotate it with the “@Entity” annotation. You can also identify which field is the primary key with “@PrimaryKey” and set autoGenerate to true.

@Entity(tableName = TABLE_NAME)
public class Task {
    @PrimaryKey(autoGenerate = true)
private int id;
private String title;
private String description;
    ...
}

DAO

The Data Access Object (DAO) is an interface annotated with Dao. This is where the database CRUD (create, read, update and delete) operations are defined. Each method is annotated with @Query, @Insert, @Update or @Delete.

@Dao
public interface TaskDao {
    // Select all from Task table and order by "complete by" date
@Query("SELECT * FROM " + Task.TABLE_NAME + " ORDER By " + Task.COMPLETE_BY_DATE)
LiveData<List<Task>> getAllTasks();
    // Select one task from Task table by id
@Query("SELECT * FROM " + Task.TABLE_NAME + " WHERE id=:id")
LiveData<Task> getTaskById(String id);

...
}

Database

Create a class that extends from RoomDatabase and annotate it with “@Database”. This class ties together the Entities and DAOs. The database instance can be created at runtime, by calling Room.databaseBuilder() (on device) or Room.inMemoryDatabaseBuilder() (in memory).

@Database(entities = {Task.class}, version = 1)
@TypeConverters(DateConverter.class)
public abstract class TaskDatabase extends RoomDatabase {

public abstract TaskDao taskDao();

public static TaskDatabase sInstance;

// Get a database instance
public static synchronized TaskDatabase getDatabaseInstance(Context context) {
if (sInstance == null) {
sInstance = create(context);
}
return sInstance;
}

// Create the database
static TaskDatabase create(Context context) {
RoomDatabase.Builder<TaskDatabase> builder = Room.databaseBuilder(context.getApplicationContext(),
TaskDatabase.class,
Task.TABLE_NAME);
return builder.build();
}
}

Overall I find it fairly easy to create the Entity, DAO and database. Definitely less boiler-plate code for creating a SQLite database. Room is also much more robust in terms of being able to validate SQLite statements during compile time and handle database migration.

ViewModel

Create a ViewModel class that extends from ViewModel. Extend from AndroidViewModel if you need the Application Context to be available.

public class TaskListViewModel extends AndroidViewModel {

private LiveData<List<Task>> mTasks;
private TaskDao mTaskDao;

public TaskListViewModel(Application application) {
super(application);

mTaskDao = TaskDatabase.getDatabaseInstance(application).taskDao();
mTasks = mTaskDao.getAllTasks();
}

// Use LiveData for getting all the data from the database
public LiveData<List<Task>> getTasks() {
return mTasks;
}

}

For those of you who are already using the MVVM architecture on Android, you know the ViewModel class can be just a POJO. What is the motivation for extending from the new ViewModel object then? I’m listing a few of them here:

  • The ViewModel is lifecycle aware so that it will survive the configuration change. It will outlive the Activity or Fragment.
  • Another motivation is easier communications between fragments, in stead of relying on the hosting Activity passing the communications.
  • Works pretty well with LiveData, an observable data holder.
  • You can use RxJava instead of LiveData.
  • AsyncTask is now safe to be used in a ViewModel!

View

Your Activity and Fragments are the Views. Don’t get this confused with the Android View object. Here the word “View” means UI.

In your Activity or Fragment, use ViewModelProviders’ static get() method to get an instance of the ViewModel.

// Create a TaskListViewModel instance
TaskListViewModel taskListViewModel = ViewModelProviders.of(this).get(TaskListViewModel.class);
// Get the task list and set the task list for the adapter
taskListViewModel.getTasks().observe(this, new Observer<List<Task>>() {
@Override
public void onChanged(@Nullable List<Task> tasks) {
adapter.setTaskList(tasks);
}
});

Overall I find the architecture components library does a good job solving the major challenges on Android:

  • MVVM architecture provides clear separation of UI from Data which also makes testing much easier.
  • Less boiler plate code in writing SQLite Db.
  • Many more options in asynchronous operations in querying data from SQlite Database. You can use LiveData, RxJava, or AsyncTask.
  • Good integration with RxJava which has been popular in the Android community for the past few years.

There are quite a lot of new terminologies and new objects introduced in the Android Architecture Components. Once you understand how all these objects fit with each other, it should be beneficial long term. In addition, you don’t need to adopt all the components in your app. Just choose the ones that make sense.


References & Learning Resources