Creating a simple todo app in Android Part II: LiveData & ViewModel

Maxime Dupierreux
ProAndroidDev
Published in
3 min readApr 11, 2019

--

In the first part, we saw how to create the data model of our app, this time, we’re going to use LiveData & ViewModel to display our todos.

What’s a ViewModel ?

This is the definition you can find here:

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModelclass allows data to survive configuration changes such as screen rotations.

So basically, ViewModel is here to save you lot of problems when dealing with the Android application lifecycle.

Activity lifecycle vs ViewModel — source: Google

What’s a LiveData ?

This is what you can find on the Android Developer website:

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

When the LiveData is updated, observers are notified and we can, for example, update the view accordingly.

The ViewModel

This is the TodoViewModel class, it extends AndroidViewModel.

class TodoViewModel(application: Application) : AndroidViewModel(application){
private val todoRepository : TodoRepository
val todos : LiveData<List<Todo>>
private var job = Job()
private val coroutineContext: CoroutineContext get() = job + Dispatchers.Main
private val scope = CoroutineScope(coroutineContext)
init {
val todoDao = TodoRoomDatabase.getDatabase(application).todoDao()
todoRepository = TodoRepository(todoDao)
todos = todoRepository.todos }
fun insert(todo: Todo) = scope.launch(Dispatchers.IO) {
todoRepository.insert(todo)
}
fun toggleDone(todo : Todo, checked : Boolean) = scope.launch(Dispatchers.IO){
todo.done = checked
todoRepository.update(todo)
}
override fun onCleared() {
super.onCleared()
job.cancel()
}
}

First, we create a LiveData object “todos”. It will contain the list of our todos.

Next, we have some objects for the use of coroutines. As in the first part, you can check here and here for more info.

The init block, it contains the code we want to be executed when the ViewModel is initialized. In our case we use it to get our todoDao and use it to initialize our repository. We also set the LiveData.

Then, we have several methods calling the repository in order to insert or update the todos. We also override the onCleared() method and call job.cancel() to cancel still running jobs.

The Activity

I won’t paste the entire activity class here. But I’ll show you the relevant parts of it.

//in onCreate
todoViewModel = ViewModelProviders.of(this).get(TodoViewModel::class.java)

Here, we’re getting the TodoViewModel from the ViewModelProvider.

//also in onCreate
todoViewModel.todos.observe(this, Observer { todos ->
todos?.let{
adapter.setTodos(todos)
}
}
)

When we have the todoViewModel we observe itstodos object. In our case every time the object is modified, we update the adapter with the updated todos.

//onCreate again
fabAdd.setOnClickListener {
if(!TextUtils.isEmpty(editTodo.text)){
val todo = Todo(0, editTodo.text.toString(),"",false)
todoViewModel.insert(todo)
}
}

Lastly, we add a listener to a FloatingActionButton and we call the insert() method from todoViewModel. The new todo will be inserted in the database using Room and the liveData observers will be notified with a list of todos containing the newly created todo.

The final architecture of our app — source

That’s it for the second part of the app. I didn’t paste all the code (especially the Activity or Layout) I’ll share the entire app on Github after the publication of the last part.

In the third and last part, we’ll create the bubble notification allowing you to access all of your todos from everywhere on your phone.

--

--