0

I have a service that I need to meet the following conditions

1) The service needs to be running while the app is in the foreground.

2) When the activity rotates, do not stop the service

3) The service must remain active even if you minimize the app when one specific fragment is open.

4) When the activity is stopped (not via rotate) and it is not the specific fragment, stop the service.

I'm starting/stopping my services in the onstart/onstop. I ended up using a boolean to track if the screen was rotating.

I'm starting/stopping my services like this

context.getApplicationContext().startService(intent)

context.getApplicationContext().stopService(intent)

It worked perfectly except for one situation.

When you turn the screen off in landscape, Android for some reason decides it needs to recreate the whole activity, and calls through the entire life cycle. I've reproduced this in a basic app on 4.4 and 5.0.1, and it's been shown in other threads such as this.

Problems understanding the life cycle when screen goes off and on

When it does this, the rotating/not rotating can't be relied on, and I'm guessing due to how quickly things were happening (multiple onStop/onStarts), it was causing race conditions. When I would return to the app, sometimes it worked, sometimes it would hang (no errors), sometimes two services would be running.

It became a rabbit hole where I started doing checks like, was the screen off when it came into the onStart, so the next time onStart was called it knew it was coming back in from an screen off recreation that was being flagged as rotated (since Android was now rotating it back into landscape from the portrait lock screen). It was getting ridiculous, and not working well.

One thread suggested using a headless fragment for it to survive rotation, but I still need it to stop when the app is closed, so I end up getting stuck with the same problem.

Are there any recommended ways to go about solving this? The service can't be allowed to remain active if the app is closed (except for the 1 fragment), it'll just be a huge battery drain, and it needs to survive rotations or it will interrupt things.

Community
  • 1
  • 1
Ben987654
  • 3,280
  • 4
  • 27
  • 50

1 Answers1

-3

Why use a service if you don't need it to be running when the app is closed? Just fire off a background thread when the app starts and do your work there.

UPDATE: Instead of starting your service from an Activity, you could start it from your Application class (or a descendant thereof). You could stop it when your app is backgrounded or killed and then start it again if the app is resumed.

The most common way to do this is to keep track of how many Activities are currently in the resumed state. This means in every Activitie's onPause() and onResume() you decrement or increment this counter. If the counter is at 0, you know you app is in the background. Note: you probably want to add a delay to decrementing the counter in onPause() since there will of course be times when you transition from one Activity to another (or switch orientation) where Activity A will be paused before Activity B starts.

Karim Varela
  • 7,562
  • 10
  • 53
  • 78
  • It's a 3rd party library that's working with some hardware over bluetooth. The service does the communication to the hardware, and on one specific fragment, it must remain active. It's used in other fragments as well, but it's okay if it's shut down on those screens. – Ben987654 Mar 23 '15 at 22:30
  • How would I turn it off though? From my understanding, there is no application life cycle, only the activities. Using Application.ActivityLifecycleCallbacks would still be triggered off the activity lifecycle as well, and I'd run into the same problem when I turn the screen off in landscape mode? – Ben987654 Mar 23 '15 at 22:54
  • Yea, it's not ideal. The most common way to do it is to keep track of how many Activities are currently in the resumed state. This means in every Activities onPause() and onResume() you decrement or increment this counter. If the counter is at 0, you know you app is in the background. Note: you probably want to add a delay to decrementing the counter in onPause() since there will of course be times when you transition from one Activity to another (or switch orientation) where Activity A will be paused before Activity B starts. – Karim Varela Mar 23 '15 at 22:59
  • Okay, I'll give that a shot and report back. Thanks! – Ben987654 Mar 23 '15 at 23:00
  • Looks like this might be working, I had to use a 600ms delay or I'd still get some weird behavior with the landscape screen off. I'll do some more testing tomorrow to make sure everything's still good. – Ben987654 Mar 24 '15 at 00:27
  • It's working well now. The main fix was actually your comment on counting the resumed activities and specifically having a delay, otherwise I still had my same problem. Do you want to update the answer with that comment or can I mark the parent answer as correct? – Ben987654 Mar 24 '15 at 19:14
  • Adding delays to logic should be violations of stackoverflow T&C – Fen1kz Jun 15 '16 at 09:11