27

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?

Androider
  • 435
  • 1
  • 9
  • 22
  • what do you mean by it " and inside this service I react to activity onResume"? – isamirkhaan1 Oct 03 '18 at 12:47
  • I mean that inside service I need to let's say connect to GPS when activity is resumed – Androider Oct 03 '18 at 13:12
  • @user1275995 please check updated answer – Jeel Vankhede Oct 05 '18 at 11:03
  • Your ViewModel lifecycle should be independent of ActivityLifeCycle. Activity has to register to UiData stream from your ViewModel, so there is not any operation needed on the Resume/Pause. Your ViewModel can react to Activity or Fragment, subscribe/unsubscribe to the stream. So for example GPS, your ViewModel could register for update when the first customer connect to the stream, and unregister when no customer are waiting for data... You can also use LiveData and its related method onActive/onInactive – Anis BEN NSIR Oct 09 '18 at 10:25

3 Answers3

18

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.

Jeel Vankhede
  • 11,592
  • 2
  • 28
  • 58
  • 1
    the bad thing in your solution is that we don't have a viewmodel anymore, I mean now activity or fragment updates ui based on mLocationUpdates, it directly interacts with mLocationUpdates which I believe is bad thing ViewModel should interact with mLocationUpdates and provide some observable for activity/fragment to let it update it's state – Androider Oct 03 '18 at 22:44
  • need help here--> https://stackoverflow.com/questions/64371863/onresume-does-not-worked-in-viewmodel – android dev Oct 15 '20 at 13:58
17

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);
  }
}
farid_z
  • 1,673
  • 21
  • 11
2

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

Muhammad Ahmed
  • 144
  • 1
  • 8