4

I currently have an app with a ForegroundService for all server/API interactions, and a Room database for local persistence. I have been trying to implement an AndroidViewModel to help with data persistence and quick UI refreshes.

However, as per the documentation, ViewModels can't be implemented in Services, and so far I've used the Service to update information locally and notify components using LocalBroadcasts (which is what I want to eliminate using ViewModels and Observers).

I need to have the service running as the app needs to keep running in the background (its a mission critical app and the app getting closed means that the user will not be available to provide critical services), and update certain information on a periodic basis (nearby requests, etc. ).

So to ask the core question -

  1. How do I separate the service from the ViewModel, and if the service has the latest synced data from the servers, how do I update the (Mutable)LiveData lists in my ViewModel?
  2. This article and this answer to a question here on SO says it is better to separate the ViewModel from the Repository while this other one gives an example of including the Room database inside the ViewModel. Which is the better option?

Some of my ViewModel code is as follows:

 public class HouseCallViewModel extends AndroidViewModel {

        private String TAG = HouseCallViewModel.class.getSimpleName();

        private MutableLiveData<List<HouseCall>> housecallList;
        private MutableLiveData<List<HouseCall>> openHousecalls, confirmedHousecalls, closedHousecalls, missedHousecalls, userCancelledHousecalls, respCancelledHousecalls;
        private MutableLiveData<List<Incident>> incidentList, openIncidents;
        private MutableLiveData<List<Incident>> closedIncidents, usercancelIncidents, respcancelIncidents;
        RevivDatabase database;
        Context context;


        public HouseCallViewModel(Application application) {
            super(application);

            //      DANGER WILL ROBINSON                                            
            context = application.getApplicationContext();
            database = Room.databaseBuilder(this.getApplication(),
                    RevivDatabase.class, application.getResources().getString(R.string.database)).build();
        }
        public LiveData<List<HouseCall>> getHousecallList() {
                if (housecallList == null) {
                    housecallList = new MutableLiveData<>();
                    loadHousecalls(); // need to call API and sync
                }
                return housecallList;
            }
       public LiveData<List<HouseCall>> getIncidentList() {
                    if (incidentList == null) {
                        incidentList = new MutableLiveData<>();
                        loadIncidents(); // need to call API and sync
                    }
                    return housecallList;
                }

    // other constructors, getters and setters here, and functions to update the data
    }
kilokahn
  • 1,136
  • 2
  • 18
  • 38

1 Answers1

5

1)

As you did not provide code details about your Service and its related components, this answer is abstract.

To separate the ViewModel from the Service, create an Activity that will access the ViewModel; you will have an Activity, a ViewModel, and a Service.

This means that you will create a bound Service (https://developer.android.com/guide/components/services#CreatingBoundService and, more specifically, https://developer.android.com/guide/components/bound-services). A bound Service provides an interface that an Activity can use to interact with the Service.

A good example of a bound Service is Google's Location Update Service: https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice

In your instance, the Service will be tasked with originating data and transmitting that data to the Activity which will then provide that data to the ViewModel.

To transmit data from the Service to the ViewModel, I suggest using Greenrobot's EventBus (http://greenrobot.org/eventbus/documentation/how-to-get-started/).

Whenever you want the Service to transmit data to the ViewModel, a single-line call to EventBus in your Service will transmit the data to the Subscribers in the Activity that are listening for that type of data.

The Activity, upon receipt of data, will then update the ViewModel with the data. Any Observers registered with the ViewModel will then receive the latest data.

2)

The Separation of Concerns principle militates in favor of separating the ViewModel from the repository. The ViewModel should be concerned with only keeping state of data that will be displayed to the user, and keeping such state across configuration changes.

loroz
  • 181
  • 1
  • 4
  • Thanks so much for answering. I am sorry I couldn't be more detailed - I didn't know what to include and what to leave out, as I was worried my question would be bloated beyond recognition. If you could specify the information you would like, I can update the question. – kilokahn Jul 23 '18 at 21:35
  • Also, would the ViewModel be effective if I updated/modified the data in the Room database from the Service, and set an Observer to ObserveForever on a LiveData list returned by the DAO? – kilokahn Jul 23 '18 at 21:37
  • 1
    No problem! I would've sought clarity in the comments before posting an answer, but I didn't have the rep for it. As to your third question, the article you linked (https://medium.com/google-developers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54) has a section titled "LiveData in Repositories." Based on that, it looks as though you can observe data changes to a database. If you wish to keep the same app structure that you already have (i.e., Service and database), then I'd suggest going down that path, though it is beyond my knowledge at this point. – loroz Jul 23 '18 at 22:02
  • 1
    I looked at implementing your solution - sending the data back to the activity and then updating the database there - but decided in favor of the third route creating a Singleton database access class, and then updating the repo from the service itself. Since my ViewModel has a DAO with functions returning LiveData> my Activity uses the ViewModel to access live data. Thanks so much for the insightful answer though. If you could edit your answer and add this as a solution, I would be happy to accept it :) – kilokahn Jul 27 '18 at 20:22