This will be a continuation of the article:
The Evolution of MV* Patterns in Android: Part 1
MVVM is also known as model-view-binder and was created by Microsoft architects Ken Cooper and John Gossman.
Google I/O 2017 introduced architecture components that includes LiveData
and ViewModel
which facilitates developing Android apps using the MVVM pattern.
This approach allows you to connect view elements with phenomena and events of the View model. It can be argued that each layer of this pattern does not know about the existence of another layer.
The first and main difference is in the names of the business logic layers: in MVP and its derivatives, it is stored in the Presenter, while in MVVM, this role is played by the ViewModel. Secondly, this is the way the View layer and ViewModel interact. This is complicated by the fact that the ViewModel
obviously does not know about the View
layer, but still controls it. How can this be? More on this a little further.
Layers of MVVM pattern are:
To implement MVVM, you will need to use LiveData
or Kotlin Flow
, which precisely implements the principle of the Observer pattern and LiveData
is also consistent with the lifecycle of the View. Learn more about working with events:
ViewModel
, which allows us to use its methods.MutableLiveData
or MutableStateFlow
. We will always have the current value of our specific widget or the entire View layer on the View layer.
ViewModel
class MainViewModel : ViewModel() {
private val booksRepository: BooksRepository = BooksRepositoryImpl()
private val _booksLiveData = MutableLiveData<List<Book>>()
val bookLiveData: LiveData<List<Book>>
get() = _booksLiveData
private val _loadingLiveData = MutableLiveData<Boolean>()
val loadingLiveData: LiveData<Boolean>
get() = _loadingLiveData
fun fetchBooks() {
_loadingLiveData.value = true
booksRepository.fetchBooks {
_booksLiveData.value = it
_loadingLiveData.value = false
}
}
}
Repository
interface BooksRepository {
fun fetchBooks(): List<Book>
fun saveInFavorites(id: Long): Boolean
fun removeFromFavorites(id: Long): Boolean
}
data class Book(val id: Long, val title: String, val author: String)
MainFragment
class MainFragment : Fragment(R.layout.fragment_main) {
private lateinit var booksRV: RecyclerView
private lateinit var progressBar: ProgressBar
private val adapter = BooksListAdapter()
private val mainViewModel: MainViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
booksRV = view.findViewById(R.id.books_rv)
progressBar = view.findViewById(R.id.progress_bar)
mainViewModel.fetchBooks()
observeLiveData()
}
private fun observeLiveData() {
mainViewModel.loadingLiveData.observe(viewLifecycleOwner) { value ->
value?.let {
if (it) {
progressBar.visibility = View.VISIBLE
} else {
progressBar.visibility = View.GONE
}
}
}
mainViewModel.bookLiveData.observe(viewLifecycleOwner) { value ->
value?.let {
adapter.submitList(it)
}
} }
companion object {
@JvmStatic
fun newInstance() = MainFragment()
}
}
With the right and experienced approach, MVVM simplifies the process of writing tests for code (due to fewer explicit connections), and it can be a slightly less verbose architecture than MVP and its derivatives.