paint-brush
The Evolution of MV Patterns in Android: Part 2by@azamatnurkhojayev
180 reads

The Evolution of MV Patterns in Android: Part 2

by Azamat NurkhojayevSeptember 9th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Google I/O 2017 introduced architecture components which includes LiveData and ViewModel which facilitates developing Android app using MVVM pattern.
featured image - The Evolution of MV Patterns in Android: Part 2
Azamat Nurkhojayev HackerNoon profile picture

This will be a continuation of the article:

The Evolution of MV* Patterns in Android: Part 1

MVVM

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:

  • Model: This layer is responsible for the abstraction of the data sources. Model and ViewModel work together to get and save the data;
  • View: The purpose of this layer is to inform the ViewModel about the user’s action. This layer observes the ViewModel and does not contain any kind of logic;
  • ViewModel: It exposes those data streams which are relevant to the View. Moreover, it serves as a link between the Model and the View;


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:


  1. Listening to events occurs simply through callbacks. View knows explicitly and stores a link to the ViewModel, which allows us to use its methods.
  2. Changes to the ViewModel occur through MutableLiveData or MutableStateFlow. We will always have the current value of our specific widget or the entire View layer on the View layer.


Example:

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()  
    }  
  
}


In conclusion

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.