ViewModel

ViewModel 

  Abstract class that holds your app's UI data. Survives configuration changes.
Previously all of your UI data is in the fragment when your app undergoes configuration changes such as rotating the phone, the activity and all the fragments in it are destroyed and rebuilt by the OS which is means unless you do something the data is lost, we've seen that onSaveInstanceState() is not often the right solution if you have a lot of complicated data, so enter the viewModel instead of having the UI data in the fragment, move it to your ViewModel and have the fragment reference the ViewModel.
ViewModel survives the configuration changes because even on all the data are in ViewModel even if you recreate the UI Controller.
ViewModel has no restrictions on size so you can store lots of data here.

ViewModels are made for and associated with a single fragment or activity.

Steps of Adding a ViewModel 

  1. Add dependency
  2. Subclass ViewModel 
  3. Associated UI Controller and ViewModel 
1. Add Lifecycle-extension gradle dependency

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 
2. Subclass ViewModel
import android.util.Log
import androidx.lifecycle.ViewModel

class WordGameViewModel : ViewModel() {
    init {
        Log.i("WordGameViewModel","WordGameViewModel created")
    }

    override fun onCleared() {
        super.onCleared()
        Log.i("WordGameViewModel","WordGameViewModel destroyed")
    }
}
ViewModel get destroyed with the fragment or activity that the ViewModel is associated with is finally and completely destroyed. 


3. Associated the UI controller and ViewModel

private lateinit var viewModel: PlayViewModel

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
playBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_play, container, false)
viewModel = ViewModelProvider(this).get(PlayViewModel::class.java)
return playBinding.root
}

Note* We never should construct ViewModel ourself, if you did you end of constructing ViewModel every time the fragment was recreated, which wouldn't solve our rotation issues. So instead the Lifecycle library creates the ViewModel for you, we request it from a class called ViewModelProvider().get(), which provides with correct ViewModelProvider
Here first we pass in the fragments, and second we pass the specific ViewModelClass that we want here.
Here each time we call a new instance of ViewModel is created. That instance of ViewModel will be associated with the fragment you pass.
When the ViewModelProvider().get() is called again it will return the reference to the pre-existing ViewModel associated with that fragment. This is what reestablishes the connection between the same ViewModel.



ViewModel vs. UI Controller 


What code to actually put in ViewModel ? 

Figuring out what actually goes in ViewModel and what actually goes in UI Controller can be tricky but it can be summarised as below: 

UI Controller or Fragments or Activity should contain :  
  • code need to display the proper values on screen and the code which captures user events. 
  • It should not hold UI data that we want to survive rotation or configuration changes. 
  • It should not contains any of the code that processes the UI data.
  • The UI Controller is the one that instantiates and contains the Views. 

ViewModel should contain : 
  • Data for UI 
  • It contains code that processes the UI data. 
  • ViewModel do not reference fragments, Activities or Views. 

Use Case :


First create the ViewModel class 

// get the view model
viewModel = ViewModelProvider(this).get(GameViewModel::class.java)

Move all the data for UI. Because this should be preserved on config changes. 

// The current word
private var surname = ""

// The current score
private var score = 0

// The list of words - the front of the list is the next word to guess
private lateinit var surnameList: MutableList<String>

We don't move binding, because it contains reference to all the Views. We use it to set onClickListeners() and update the UI. This is the task of UI Controller.  

Move making decision methods: 

private fun nextSurname() {
// select and remove a surname form the list
if (surnameList.isEmpty()){
gameFinished()
} else {
surname = surnameList.removeAt(0)
}
updateSurnameText()
updateScoreText()
}

private fun resetList() {
surnameList = mutableListOf(
"Elizabeth",
"Clinton",
"Bush",
"Merkel",
"Trump",
"Hillary"
)
surnameList.shuffle()
}

I wan't make a initialisation when the ViewModel gets created not every time when fragment gets recreated. So we move this to initialisation block of ViewModel. 

resetList()
nextSurname()

Understand that any navigation we do should be done in UI Controller. 

Move also those methods: 

private fun onSkip(){
score--
nextSurname()
}

private fun onCorrect(){
score++
nextSurname()
}












Comments

Popular Posts