This is my MWE test class, which depends on AndroidX, JUnit 4 and MockK 1.9:
class ViewModelOnClearedTest {
#Test
fun `MyViewModel#onCleared calls Object#function`() = mockkObject(Object) {
MyViewModel::class.members
.single { it.name == "onCleared" }
.apply { isAccessible = true }
.call(MyViewModel())
verify { Object.function() }
}
}
class MyViewModel : ViewModel() {
override fun onCleared() = Object.function()
}
object Object {
fun function() {}
}
Note: the method is protected in superclass ViewModel.
I want to verify that MyViewModel#onCleared calls Object#function. The above code accomplished this through reflection. My question is: can I somehow run or mock the Android system so that the onCleared method is called, so that I don't need reflection?
From the onCleared JavaDoc:
This method will be called when this ViewModel is no longer used and will be destroyed.
So, in other words, how do I create this situation so that I know onCleared is called and I can verify its behaviour?
In kotlin you can override the protected visibility using public and then call it from a test.
class MyViewModel: ViewModel() {
public override fun onCleared() {
///...
}
}
I've just created this extension to ViewModel:
/**
* Will create new [ViewModelStore], add view model into it using [ViewModelProvider]
* and then call [ViewModelStore.clear], that will cause [ViewModel.onCleared] to be called
*/
fun ViewModel.callOnCleared() {
val viewModelStore = ViewModelStore()
val viewModelProvider = ViewModelProvider(viewModelStore, object : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T = this#callOnCleared as T
})
viewModelProvider.get(this#callOnCleared::class.java)
//Run 2
viewModelStore.clear()//To call clear() in ViewModel
}
TL;DR
In this answer, Robolectric is used to have the Android framework invoke onCleared on your ViewModel. This way of testing is slower than using reflection (like in the question) and depends on both Robolectric and the Android framework. That trade-off is up to you.
Looking at Android's source...
...you can see that ViewModel#onCleared is only called in ViewModelStore (for your own ViewModels). This is a storage class for view models and is owned by ViewModelStoreOwner classes, e.g. FragmentActivity. So, when does ViewModelStore invoke onCleared on your ViewModel?
It has to store your ViewModel, then the store has to be cleared (which you cannot do yourself).
Your view model is stored by the ViewModelProvider when you get your ViewModel using ViewModelProviders.of(FragmentActivity activity).get(Class<T> modelClass), where T is your view model class. It stores it in the ViewModelStore of the FragmentActivity.
The store is clear for example when your fragment activity is destroyed. It's a bunch of chained calls that go all over the place, but basically it is:
Have a FragmentActivity.
Get its ViewModelProvider using ViewModelProviders#of.
Get your ViewModel using ViewModelProvider#get.
Destroy your activity.
Now, onCleared should be invoked on your view model. Let's test it using Robolectric 4, JUnit 4, MockK 1.9:
Add #RunWith(RobolectricTestRunner::class) to your test class.
Create an activity controller using Robolectric.buildActivity(FragmentActivity::class.java)
Initialise the activity using setup on the controller, this allows it to be destroyed.
Get the activity with the controller's get method.
Get your view model with the steps described above.
Destroy the activity using destroy on the controller.
Verify the behaviour of onCleared.
Full example class...
...based on the question's example:
#RunWith(RobolectricTestRunner::class)
class ViewModelOnClearedTest {
#Test
fun `MyViewModel#onCleared calls Object#function`() = mockkObject(Object) {
val controller = Robolectric.buildActivity(FragmentActivity::class.java).setup()
ViewModelProviders.of(controller.get()).get(MyViewModel::class.java)
controller.destroy()
verify { Object.function() }
}
}
class MyViewModel : ViewModel() {
override fun onCleared() = Object.function()
}
object Object {
fun function() {}
}
For Java, if you create your test class in the same package (within the test directory) as of the ViewModel class (here, MyViewModel), then you can call onCleared method from the test class; since protected methods are also package private.
Related
I am trying to understand how can I use my same viewmodel across different fragments which even belongs to different activities.
So let's say I have Activity1 with Fragment A, Fragment B and Activity2 with Fragment C. How do I create a single instance of viewmodel that I can use across all these fragments.
I tried understanding shared viewmodel but seems like it is to be used if sharing data between fragments of a single activity and not multiple activities.
So basically I want to create a single instance of viewmodel across all the fragments? How can I achieve this functionality also keeping in mind the MVVM approach.
Any help would be appreciated. Thanks!
This is not supported. Google's recommendation is to put all your screens in a single Activity.
But, you can make an intermediate singleton class that each instance of the ViewModel uses.
Or maybe you could use a factory that treats it like a temporary singleton and does reference counting so it doesn't get cleared too early or hang onto the reference for too long. Untested example of what I mean:
private var viewModelInstance: MyViewModel? = null
private var refCount = 0
class MyViewModel: ViewModel() {
override fun onCleared() {
if (--refCount > 0) {
return
}
viewModelInstance = null
// Do typical onCleared cleanup here
}
}
class MyViewModelFactory: ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
require(modelClass.isAssignableFrom(MyViewModel::class.java)) { "Factory only supports MyViewModel" }
refCount++
viewModelInstance = viewModelInstance ?: MyViewModel()
return viewModelInstance as T
}
}
Shared viewmodel between multiple activities is not supported.
One way to achieve this using AndroidViewModel. You can create a ViewModel extending AndroidViewModel . This requires application instance. This viewmodel will be binded to application lifecycle and same instance will be available through out the lifecycle of the application. In one activity you can add data, and in other activity you can get updated data.
This will be acting something like singleton instance(But not exactly).
Addition to this, you can also use live data in AndroidViewModel if you use observer with activity/fragment lifecycle owner. So the observer will be live only till life cycle of fragment or activity.
ViewModel:
class AppViewModel constructor(private val mApplication: Application) :
AndroidViewModel(mApplication) {
//ViewModel Logic
}
Initializing Viewmodel:
You can initialize viewmodel like this in any fragment or activity.
val appViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(MyApp.getInstance())
.create(AppViewModel::class.java)
Application Class:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: CooperApp
#Synchronized
fun getInstance(): MyApp {
return mInstance
}
}
}
Also one more thing we can use is like initializing viewmodel in application class and create similar function to getInstance() which will return viewmodel instance and use it all over the app
I'm excited to use the new Android Architecture Components ViewModel system, which nicely separates the Activity/Fragment/Layout rendering concerns from the ViewModel logic. I've successfully unit-tested the ViewModel in isolation, and now would like to try out some screenshot testing by providing mocked ViewModels to the Activity/Fragment for various state scenarios.
I've successfully configured my androidTests to be able to use Mockito in my device tests, that part works great.
However, the officially recommended way of calling ViewModelProvider or delegating by viewModels<> does not seem to offer a way to inject mocked ViewModels. I'd rather not add an entire DI framework just to work around this omission in the documentation, so I wonder if anyone has any successful examples of providing mocked ViewModels with the official Android Architecture Components without the extra dependencies of Dagger or Hilt.
The only related answer from 1 year ago suggests using ActivityTestRule and manually controlling the activity lifecycle, but that Rule is deprecated in favor of activityScenarioRule which does not provide this control.
You can use a ViewModelProvider, so you can replace the ViewModelProvider.Factory in the tests with a mock. For example by using:
viewModel = ViewModelProvider(this, ViewModelFactoryOfFactory.INSTANCE)
.get(MyViewModel::class.java)
Where:
object ViewModelFactoryOfFactory {
// The default factory.
var INSTANCE: ViewModelProvider.Factory = MyViewModelFactory()
private set
// To set the factory during tests.
#VisibleForTesting
fun setTestFactory(factory: ViewModelProvider.Factory) {
ViewModelFactoryOfFactory.INSTANCE = factory
}
}
Then in the tests setup one can:
ViewModelFactoryOfFactory.setTestFactory(mockFactory)
One may argue that all this could be replaced by just the factory to get the ViewModel.
Another option could be just make the ViewModelProvider.Factory a field/property in the Activity or fragment, so it can be also set from tests, also allowing for better memory management.
I decided to rewrite the by viewModels delegate to check for instances in a map of mock ViewModels, so my activities can use the normal delegate pattern and provide their own factories if the ViewModel isn't found.
val mockedViewModels = HashMap<Class<*>, ViewModel>()
#MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
// the production producer
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return createMockedViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
/// ... and similar for the fragment-ktx delegates
/**
* Wraps the default factoryPromise with one that looks in the mockedViewModels map
*/
fun <VM : ViewModel> createMockedViewModelLazy(
viewModelClass: KClass<VM>,
storeProducer: () -> ViewModelStore,
factoryPromise: () -> ViewModelProvider.Factory
): Lazy<VM> {
// the mock producer
val mockedFactoryPromise: () -> ViewModelProvider.Factory = {
// if there are any mocked ViewModels, return a Factory that fetches them
if (mockedViewModels.isNotEmpty()) {
object: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return mockedViewModels[modelClass] as T
?: factoryPromise().create(modelClass) // return the normal one if no mock found
}
}
} else {
// if no mocks, call the normal factoryPromise directly
factoryPromise()
}
}
return ViewModelLazy(viewModelClass, storeProducer, mockedFactoryPromise)
}
I am working on a utility class that subscribes to the app going foreground/background via ProcessLifecycleOwner api. The class takes in a ProcessLifecycleOwner instance, and has the following methods which observe on lifecycle events:
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startSomething() {
appStatusSubject.onNext(AppStatus.FOREGROUNDED)
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopSomething() {
appStatusSubject.onNext(AppStatus.BACKGROUNDED)
}
I would like to write a unit test for this utility class. Is there anyway I can mock the ProcessLifecycleOwner object, have it emit certain lifecycle events, and assert that appStatusSubject.onNext(...) is called?
Would really appreciate any advice.
There is a TestLifecycleOwner, which probably does what you want.
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:lifecycle/lifecycle-runtime-testing/src/main/java/androidx/lifecycle/testing/TestLifecycleOwner.kt;l=38
It's part of the androidx.lifecycle:lifecycle-runtime-testing artifact.
If you want to create a super simple fake for the LifecycleOwner, you can do so as follows:
class TestLifecycleOwner() : LifecycleOwner {
private val registry = LifecycleRegistry(this).apply {
currentState = Lifecycle.State.RESUMED
}
override fun getLifecycle(): Lifecycle = registry
}
Then you can simply call:
testLifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
I am trying to add Dagger 2 to my project. I was able to inject ViewModels (AndroidX Architecture component) for my fragments.
I have a ViewPager which has 2 instances of the same fragment (Only a minor change for each tabs) and in each tab, I am observing a LiveData to get updated on data change (from API).
The issue is that when the api response comes and updates the LiveData, the same data in the currently visible fragment is being sent to observers in all the tabs. (I think this is probably because of the scope of the ViewModel).
This is how I am observing my data:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
activityViewModel.expenseList.observe(this, Observer {
swipeToRefreshLayout.isRefreshing = false
viewAdapter.setData(it)
})
....
}
I am using this class for providing ViewModels:
class ViewModelProviderFactory #Inject constructor(creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>?) :
ViewModelProvider.Factory {
private val creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>? = creators
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel?>? = creators!![modelClass]
if (creator == null) { // if the viewmodel has not been created
// loop through the allowable keys (aka allowed classes with the #ViewModelKey)
for (entry in creators.entries) { // if it's allowed, set the Provider<ViewModel>
if (modelClass.isAssignableFrom(entry.key!!)) {
creator = entry.value
break
}
}
}
// if this is not one of the allowed keys, throw exception
requireNotNull(creator) { "unknown model class $modelClass" }
// return the Provider
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
companion object {
private val TAG: String? = "ViewModelProviderFactor"
}
}
I am binding my ViewModel like this:
#Module
abstract class ActivityViewModelModule {
#MainScope
#Binds
#IntoMap
#ViewModelKey(ActivityViewModel::class)
abstract fun bindActivityViewModel(viewModel: ActivityViewModel): ViewModel
}
I am using #ContributesAndroidInjector for my fragment like this:
#Module
abstract class MainFragmentBuildersModule {
#ContributesAndroidInjector
abstract fun contributeActivityFragment(): ActivityFragment
}
And I am adding these modules to my MainActivity subcomponent like this:
#Module
abstract class ActivityBuilderModule {
...
#ContributesAndroidInjector(
modules = [MainViewModelModule::class, ActivityViewModelModule::class,
AuthModule::class, MainFragmentBuildersModule::class]
)
abstract fun contributeMainActivity(): MainActivity
}
Here is my AppComponent:
#Singleton
#Component(
modules =
[AndroidSupportInjectionModule::class,
ActivityBuilderModule::class,
ViewModelFactoryModule::class,
AppModule::class]
)
interface AppComponent : AndroidInjector<SpenmoApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
I am extending DaggerFragment and injecting ViewModelProviderFactory like this:
#Inject
lateinit var viewModelFactory: ViewModelProviderFactory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
....
activityViewModel =
ViewModelProviders.of(this, viewModelFactory).get(key, ActivityViewModel::class.java)
activityViewModel.restartFetch(hasReceipt)
}
the key will be different for both the fragments.
How can I make sure that only the observer of the current fragment is getting updated.
EDIT 1 ->
I have added a sample project with the error. Seems like the issue is happening only when a custom scope is added. Please check out the sample project here: Github link
master branch has the app with the issue. If you refresh any tab (swipe to refresh) the updated value is getting reflected in both the tabs. This is only happening when I add a custom scope to it (#MainScope).
working_fine branch has the same app with no custom scope and its working fine.
Please let me know if the question is not clear.
I want to recap the original question, here's it:
I am currently using the working fine_branch, but I want to know, why would using scope break this.
As per my understanding your have an impression, that just because you are trying to obtain an instance of ViewModel using different keys, then you should be provided different instances of ViewModel:
// in first fragment
ViewModelProvider(...).get("true", PagerItemViewModel::class.java)
// in second fragment
ViewModelProvider(...).get("false", PagerItemViewModel::class.java)
The reality, is a bit different. If you put following log in fragment you'll see that those two fragments are using the exact same instance of PagerItemViewModel:
Log.i("vvv", "${if (oneOrTwo) "one:" else "two:"} viewModel hash is ${viewModel.hashCode()}")
Let's dive in and understand why this happens.
Internally ViewModelProvider#get() will try to obtain an instance of PagerItemViewModel from a ViewModelStore which is basically a map of String to ViewModel.
When FirstFragment asks for an instance of PagerItemViewModel the map is empty, hence mFactory.create(modelClass) is executed, which ends up in ViewModelProviderFactory. creator.get() ends up calling DoubleCheck with following code:
public T get() {
Object result = instance;
if (result == UNINITIALIZED) { // 1
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
instance = reentrantCheck(instance, result); // 2
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
The instance is now null, hence a new instance of PagerItemViewModel is created and is saved in instance (see // 2).
Now the exact same procedure happens for SecondFragment:
fragment asks for an instance of PagerItemViewModel
map now is not empty, but does not contain an instance of PagerItemViewModel with key false
a new instance of PagerItemViewModel is initiated to be created via mFactory.create(modelClass)
Inside ViewModelProviderFactory execution reaches creator.get() whose implementation is DoubleCheck
Now, the key moment. This DoubleCheck is the same instance of DoubleCheck that was used for creating ViewModel instance when FirstFragment asked for it. Why is it the same instance? Because you've applied a scope to the provider method.
The if (result == UNINITIALIZED) (// 1) is evaluating to false and the exact same instance of ViewModel is being returned to the caller - SecondFragment.
Now, both fragments are using the same instance of ViewModel hence it is perfectly fine that they are displaying the same data.
Both the fragments receive the update from livedata because viewpager keeps both the fragments in resumed state.
Since you require the update only on the current fragment visible in the viewpager, the context of the current fragment is defined by the host activity, the activity should explicitly direct updates to the desired fragment.
You need to maintain a map of Fragment to LiveData containing entries for all the fragments(make sure to have an identifier that can differentiate two fragment instances of the same fragment) added to viewpager.
Now the activity will have a MediatorLiveData observing the original livedata observed by the fragments directly. Whenever the original livedata posts an update, it will be delivered to mediatorLivedata and the mediatorlivedata in turen will only post the value to livedata of the current selected fragment. This livedata will be retrieved from the map above.
Code impl would look like -
class Activity {
val mapOfFragmentToLiveData<FragmentId, MutableLiveData> = mutableMapOf<>()
val mediatorLiveData : MediatorLiveData<OriginalData> = object : MediatorLiveData() {
override fun onChanged(newData : OriginalData) {
// here get the livedata observed by the currently selected fragment
val currentSelectedFragmentLiveData = mapOfFragmentToLiveData.get(viewpager.getSelectedItem())
// now post the update on this livedata
currentSelectedFragmentLiveData.value = newData
}
}
fun getOriginalLiveData(fragment : YourFragment) : LiveData<OriginalData> {
return mapOfFragmentToLiveData.get(fragment) ?: MutableLiveData<OriginalData>().run {
mapOfFragmentToLiveData.put(fragment, this)
}
}
class YourFragment {
override fun onActivityCreated(bundle : Bundle){
//get activity and request a livedata
getActivity().getOriginalLiveData(this).observe(this, Observer { _newData ->
// observe here
})
}
}
I'm trying to create an app which will use MVVM architecture and there's one thing I quite don't understand.
Official Android docs say that's not a good idea to reference activity context in ViewModel's (as ViewModel may outlive activity) so I've started to wonder about usecase when I want to execute some action when my activity is resumed.
I know ViewModel's shouldn't do business logic themselves but even if I use some service class (let's say GPSService which has to start and pauseeach time activity is resumed on paused), and inside this service I react to activity onResume (using Lifecycle observer) I will still reference this activity from ViewModel as I'm referencing service which holds reference to activity being observed, this may cause activity leak (correct me if I'm wrong).
So my question is, how to react to activity or fragment lifecycle in MVVM architecture?
If you need to have a ViewModel be lifecycle aware, then you can have it implement LifeCycleObserver and override life cycle events as necessary. Example,
public class MyModel extends ViewModel implements
LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
protected void onLifeCycleStop() {
// do something
}
}
In the activity or fragment then you can add the view model to the activity life cycle owner.
public class MyActivity extends AppCompatActivity {
protected MyModel mMyModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyModel = ViewModelProviders
.of(this)
.get(MyModel.class);
getLifecycle().addObserver(mMyModel);
}
}
I know ViewModel's shouldn't do business logic themselves
Yes, you're right. ViewModel should not contain business logic but
it should contain UI related logic. So basically, API calls or Some
location related stuffs should be avoided in ViewModel logic.
So what if you wanna make some scenario which can react to any activity lifecycle? I'll suggest you to use LifecycleObserver.
Why?, Because LifecycleObserver will provide you callbacks once it's LifecycleOwner will change it's state.
What is LifecycleOwner here? In our case it may be Activity/Fragment.
So, how you can achieve this?
Let's say you want to make location requests during resume & pause period of any activity.
So, for that you can create a class called LocationUpdates as LifecycleObserver like below:
class LocationUpdates : LifecycleObserver {
constructor(){
// some basic location related initialization here
}
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
// this method will respond to resume event of our Lifecycle owner (activity/fragment in our case)
// So let's get location here and provide callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
// this method will respond to pause event of our Lifecycle owner (activity/fragment in our case)
// So let's stop receiveing location updates here and remove callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) // Optional if you want to cleanup references
fun cleanUp() {
// this method will respond to destroy event of our Lifecycle owner (activity/fragment in our case)
// Clean up code here
}
}
Now from your activity, you can directly make your LocationUpdates, and receive callback.
class MyActivity : AppCompatActivity() {
private lateinit var mLocationUpdates: LocationUpdates
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Initialize your LifecycleObserver here & assign it to this activity's lifecycle
lifecycle.addObserver(mLocationUpdates)
}
}
You can refer to how to handle Lifecycle & Codelabs example.
Edit:
If you want to have ViewModel for that job, consider this:
class MyViewModel : ViewModel {
private lateinit var mLocationUpdates: LocationUpdates
constructor() : super() {
// initialize LocationUpdates here
}
// Assign our LifecyclerObserver to LifecycleOwner
fun addLocationUpdates(lifecycle: Lifecycle){
lifecycle.addObserver(mLocationUpdates)
}
//Optional, we really don't need this.
fun removeLocationUpdates(lifecycle: Lifecycle){
lifecycle.removeObserver(mLocationUpdates)
}
}
If your LocationUpdates depends upon Context, consider using AndroidViewModel.
We can now observe our location updates # any activity/fragment using LiveData, and assign our LifecycleObserver like below:
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by lazy {
return#lazy ViewModelProviders.of(this#MyActivity).get(MyViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.addLocationUpdates(lifecycle)
}
}
Please note: there's still lot to cover but making this answer as short as possible. So, if you're still confused about something related then please feel free to ask me in comment. I will edit my answer.
with java 8 LifecycleObserver has been deprecated. According to the [docs][1] it is not recommended to use this class as it uses reflection.
Rather the docs recommend using DefaultLifecycleObserver. To do that, extend your ViewModel class with DefaultLifecycleObserver like:
class MyViewModel : ViewModel(), DefaultLifecycleObserver {//implement default lifecycle observer
override fun onCreate(owner: LifecycleOwner) {//override lifecycle events
super.onCreate(owner)
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
}
}
and get all the lifecycle event callbacks in your viewmodel by registering your viewmodel as lifecycle event observer in your view class (e.g. Activity class) like:
class MyActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycle.addObserver(splashViewModel)//registering observer
...
}
}
its just and update to the answer by #farid_z with kotlin and new sdk.
[1]: https://developer.android.com/reference/androidx/lifecycle/LifecycleObserver