It seems Android in recent version have introduced multiple restrictions on running services in the Background, which of course it good news for battery life of our devices. My goal is to create an app which adheres to this by only running a service as long as the User is interacting with the app (which seems to be what they are aiming for). But it's anything but clear to me how you can implement this properly. My requirements are as follows:
The Service should be started as soon as any Activity becomes visible to the user.
The Service should stay uninterrupted in the running state while the user is interacting with the app (browsing between activities).
When the UI (regardless of what activity was active) is sent to the background, the Service should run for 2-3 seconds and then stop itself. The 2-3 seconds are needed for a clean shutdown of the service.
The service can be started via a Push message when the app is in the background (or closed), in order to handle incoming events at any time. The service then registers to the Remote Server, and checks for updates. If there are updates, a notification is given to the user. Then the service once more shuts back down after 2-3 seconds of inactivity.
To me, it seems a Bound Service is what is intended to be used. But it's not clear to me how my requirements will fit with the Bound Service model. Is there anyone who have any experience with this, who can point me in the right direction?
EDIT: The "Service" in this case is a Local, In-Process service, which is not intended to be accessed externally.
(This answer presumes a local, "in-process"
Service, which is no doubt what you intend to use.)For your use case, you actually use a combination of techniques to keep the
Servicerunning.Use the "bound
Servicemodel" to keep theServicearound while any one of yourActivitiesare visible. It's simple enough; callbindService()inActivity.onStart(), andunbindService()inActivity.onStop(). If you're worried about theServicebeing destroyed in the brief moment of transition between two of yourActivities, don't be; Android is smart enough to wait for the lifecycle changes of different application components to "settle" before it decides aServiceis unreferenced/unneeded.You should use the
BIND_AUTO_CREATEflag for all yourbindService()calls. Keep in mind that theServiceisn't created immediately upon callingbindService(); it takes a few milliseconds, and you must be careful to return control to the framework, by returning from whatever lifecycle method (e.g.,onStart()) you are currently in. Only then will you get a call toonServiceConnected().You'll need to manually keep track of how many
Activitiesare bound to yourService, in order to determine when to begin your 2-3 second cleanup logic. See this answer for a valid approach. Don't worry about theServicebeing destroyed synchronously during your lastunbindService()call -- just as withbindService(), the actual lifecycle state change is "delayed."Now the question is, how do you keep the
Servicealive for this extra 2-3 seconds, at this point (the point where you've determined the number of openActivitieshas dropped to 0)? Well, you can simply callstartService(). You can even call it from a method in yourServicesubclass; as long as you have a validContextavailable, it doesn't really matter.startService()indicates to the system that you want to keep theServicearound, irrespective of how manyActivities(or other clients) might be bound to it. In this case, theServicewon't be restarted -- it's already running!Once your cleanup is complete, you can call
stopService()or, better yet,stopSelf(). That effectively cancels thestartService()call, and tells the OS, "I'm done". Expect a call toService.onDestroy()shortly thereafter.Keep in mind that one of your
Activitiesmight pop up asynchronously, and re-bind to theService, before cleanup completes. It is an edge case, but one that is easily handled. TheServicewill only be destroyed when both these conditions are true: 1.) No clients are binding/bound, and 2.) no un-cancelled calls tostartService()exist.Note that on Oreo and later, the system can be quite aggressive about killing apps with background
Services. According to this doc, interacting with the user puts you on the whitelist for "several minutes", so I think 2-3 seconds is going to be OK. Similarly, if you're handling a "high-priority FCM message" (which is what I assume you mean by "push" message), you'll be placed on the whitelist, and allowed another few minutes in which to execute theService(this time using thestartService()/stopSelf()approach).