Android Architecture Components ViewModel - communication with Service/IntentService
Asked Answered
E

1

6

I'm exploring Google's Android Architecture Components. In my project I'm relying on Services and IntentServices. What is the correct way to communicate with app's ViewModel from an IntentService or Service? Is it achievable using LiveData?

Earvin answered 11/10, 2017 at 7:35 Comment(5)
What role in the MVVM architecture will play your service?Zoosporangium
IntentService for remote data fetch, gps location for Service.Earvin
I think, in this case, you should consider your services as a Model in MVVM architecture and the ViewModel should interact with Services (but not vice versa) to retrieve data and provide it to View (Activity or Fragment) using architecture mechanism like LiveData. Services may not know anything about ViewModel.Zoosporangium
@Zoosporangium How would viewmodel have a reference to Services? Correct me if I'm wrong but isn't that an anti-pattern since Services are part of the Android Framework. Viewmodel should never directly communicate with them. That is why there is LiveData. Plus Services and ViewModel do not have the same lifecycle. It may end up leaking one or the other? Am I wrong?About
@Archie, Yes, you're right. ViewModel shouldn't interact with Service or other Adnroid components directly. When I wrote this I didn't fully understand SOLID principles. In Lyla's post a perfect diagram with MVVM architecture.Zoosporangium
C
25

TL;DR It's achievable - use an observer relationship. Your IntentService and likely location service should not be aware of your ViewModel. Consider using a Repository. LiveData can be used (see postValue). It's good for updating the UI (ViewModel to Activity communication) because it's lifecycle-aware. When you're not updating the UI, you could consider RxJava.


It depends on what architecture you're following. If you're doing something similar to what's described in the Guide to App Architecture, your IntentService is probably started by your remote data source code:

enter image description here

Your remote data source code would have an observable (Rx Flowable, LiveData, etc) which I'll call observable A, for the data downloaded by your intent service. You Repository class (if you use one) would have an observable b and your ViewModel would have an observable c.

The Repository subscribes to the observable in your networking code (observable A), the ViewModel subscribes to the observable in your Repository (observable B), and your Activity/Fragment/View subscribes to the observable in your ViewModel (observable c). Then...

  1. IntentService gets data back and sets observable A
  2. This triggers your Repository because it's subscribed - it does the type of data processing the repository is supposed to do, like saving the data to a database.
  3. When your repository is done, it sets observable B with the newly processed data.
  4. This triggers your ViewModel because it's subscribed - it does the type of data processing ViewModels do, namely formatting the data so that it's ready for the view, then sets observable C...
  5. This triggers your Activity/Fragment/View which updates the UI

It's basically a long chain of observer relationships all the way up. At each level, the appropriate processing is done, then it sets an observable, which triggers the next level with the new data. This allows you to avoid strong coupling with your IntentService/Repository/ViewModel.

Your Services would not be aware of your ViewModel (or Repository if you have one), they should simply set the value of an observable. If you want to skip having a repository, you could have the ViewModel observe your remote data source class, but if you need to do any logic like saving the data you downloaded to a database, you probably want a Repository.

Two notes about LiveData - If you need to update LiveData when you're doing a background operation, use postValue.

LiveData is lifecycle-aware, which makes it particularly well suited for observation by things with lifecycles (Activities/Fragments). The observe method takes a LifecycleOwner.

For observers like B and A in your Repository/Networking code, there likely won't be a LifecycleOwner. This means either doing something like using observerForever, or using another observable, like an RxFlowable.

Contrabass answered 13/10, 2017 at 18:37 Comment(8)
The ViewModel guide states: ViewModel objects can contain LifecycleObservers, such as LiveData objects. However ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects.Antiparticle
This is awesome! Thanks!Snug
How should you get the system service reference to the Repository in the first place though?Rutger
What is the Remote Data Source? Is it the service itself? Is it a singleton that starts and binds to the service and remains for the life of the service? If the latter, is there some class that it should inherit, or is it just a POJO?Rowley
@Rutger exactly my question!! I don't think this answer is right.About
RxJava has a steep learning curve. Might take six months to learn. Not worth it for OP's question.Considering
This is jsut adding another unneccessary layer since the viewModel just passes the data through to the activity...Equipage
I don't agree that the data layer depends on the Android Components. I think that the repository and data source (data layer) should never know the Android components. The data layer has only one responsibility to access data.Interlude

© 2022 - 2024 — McMap. All rights reserved.