4.2. ViewModels

ViewModels were born to disconnect the model (the data used by our app) from the view (the specific way in which data is displayed on the screen). In this way, if the operating system or user makes changes that affect the visualization, the interface data is not lost because it is stored in the model.

The lifecycle of a ViewModel The lifecycle of a ViewModel.
Source: Android Developers License: CC BY 2.5

A typical example is having a form where the user has selected an image and is previewing it. When the device rotates, the selected image disappears. Sometimes this can also happen with text-based inputs. This occurs because rotating the device recreates the activity, rather than modifying it.

First, we define a class that inherits from ViewModel. In this class we define the properties we want to preserve:

class AddItemViewModel: ViewModel() {
   private var _add_description = MutableLiveData<String>()
   var add_description: MutableLiveData<String> = _add_description

   private var _add_image = MutableLiveData<Bitmap>()
   var add_image: MutableLiveData<Bitmap> = _add_image
}

In our activity, we create the instance of our model:

private val addItemViewModel: AddItemViewModel by viewModels()

The viewModels annotation indicates that the model will be retained while the fragment is alive. If you do not have fragments, you will do so while the activity is alive.

In the callback onCreate of the activity, we indicate that we want to monitor changes to the model to adapt the interface.

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   binding = ActivityAddItemBinding.inflate(layoutInflater)
   setContentView(binding.root)


   addItemViewModel.add_image.observe(this, Observer<Bitmap> {
       binding.itemImageNew.setImageBitmap(it)
   })
}

This way, we no longer directly assign the selected image to the interface but, instead, we assign it to the model. The interface will be automatically modified by the previous code.

var getResult  = registerForActivityResult(
   ActivityResultContracts.StartActivityForResult()) {
   val value = it.data?.getData()
   val inputStream: InputStream? = this.getContentResolver().openInputStream(value!!)
   val bitmap = BitmapFactory.decodeStream(inputStream)
   addItemViewModel.add_image.value = bitmap
}

When the lifecycle associated with the model ends, the system automatically deletes the instance.