8

I developed an app and some people complains that it takes too much battery, it is the second most consuming process after the screen. However, in some devices it does not consumes that much battery.

All the work my app do is in a service. The service is sticky and is running all the time (the android system may kill it when has low resources or pause it when device go to sleep), it has listener to the accelerometer as long as the screen is on, it is not foreground service and does not hold wakelock.

Can someone tell me why it takes a lot of battery? And why is this happening only on some of the devices?

Here is the relevant code:

public class aListenerService extends Service implements SensorEventListener
{
    private BroadcastReceiver mScreenReceiver = new BroadcastReceiver()
    {
        // if screen was turned on then register to accelerometer
        // if screen was turned off then unregister from accelerometer
    }

    private BroadcastReceiver mPhoneStateReceiver = new BroadcastReceiver()
    {
        // do something...
    }

    @Override
    public void onCreate() 
    {
        super.onCreate();

        // get sensor manager and accelerometer sensor
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        // register accelerometer sensor and receiver
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
        IntentFilter screenFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        screenFilter.addAction(Intent.ACTION_SCREEN_ON);
        registerReceiver(mScreenReceiver, screenFilter);
        registerReceiver(mPhoneStateReceiver, new IntentFilter(INTENT_ACTION_PHONE_STATE));
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
        super.onStartCommand(intent, flags, startId);

        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() 
    {
        super.onDestroy();

        // unregister to sensor and receivers
        mSensorManager.unregisterListener(this);
        unregisterReceiver(mScreenReceiver);
        unregisterReceiver(mPhoneStateReceiver);
    }

    @Override
    public void onSensorChanged(SensorEvent event)
    {
        // do something...
    }
}
iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
adl
  • 1,865
  • 1
  • 25
  • 32

1 Answers1

8

Running services all the time can be expensive regarding CPU and battery - that's one of the downsides of a service. In particular if you include threads that cause some CPU load. There are some options to choose from, depending on your app requirements:

  1. If the result of your service is only relevant at the time when the user can consume it you might think about stopping and starting your service on screen on and off events - or at least starting and stopping included threads/handlers. This can be done by using a BroadcastReceiver:

    public class ScreenReceiver extends BroadcastReceiver {
    
      @Override
      public void onReceive(Context context, Intent intent) {
          if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
              // (1) stop service or (2) stop all threads and unregister all event 
              // handlers, if the service has to register the screen receiver
          } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
              // (1) start service or (2) start all threads and register all event
              // handlers as needed
          }
      }
    }
    

    Note that screen events have to be registered programmatically. If you have to register the screen on and off events inside of the same service, start it as sticky. This way the service instance will be kept. (examples: battery widgets, weather information, ...)

  2. In case you have an event based environment you could think about using Google Cloud Messaging (GCM). With GCM you can send a message from any of your servers to any registered device causing it to wake up and process incoming information. No need to poll for information all the time.

    Check out Googles documentation and examples, if this scenario fits to your requirements (examples: messaging app, chat app, ...)

  3. If you need to process/run data/code regularly you might think about using an AlarmManager:

    public void SetAlarm(Context context) {
      AlarmManager manager = (AlarmManager)context.
                              getSystemService(Context.ALARM_SERVICE);
    
      Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
      PendingIntent pendingIntent = 
                    PendingIntent.getBroadcast(context, 0, intent, 0);
    
      am.setRepeating(AlarmManager.RTC_WAKEUP, 
                      System.currentTimeMillis(), 1000 *60 , pendingIntent);
    }
    

    However, the downside is that it is not a good option for short intervals (lets say smaller than 30 minutes). (examples: email client, ...)

So there are some alternatives. Looking at your code I can see you are doing something with sensors. Do you need the sensor information, if the screen is off? Or what kind of events could trigger the start and end of your service?

If you really need the results all the time (for instance for a GPS tracker), you've probably no other choice than optimizing your code. May be it make sense to stop the service, if the battery is low (see this for more details).

Good luck!

Trinimon
  • 13,839
  • 9
  • 44
  • 60
  • Thanks, I want to use option 1 but since I register to broadcast programmatically in the service then the broadcast lifetime depends on the app's lifetime, and if I stop the service when screen turns off then the app's lifetime is over and the broadcast will be unregistered and I won't be able to start the service when screen goes on. – adl Aug 11 '13 at 15:56
  • Well, if you start your service as sticky, the `Service` instance is most likely not going to be destroyed until you call `stopSelf()`, so you can register screen receivers here. There is one exception: if the system runs out of memory the service might be destroyed. Due to the sticky flag it will be restarted once the system has enough memory again (see http://stackoverflow.com/questions/9093271/start-sticky-and-start-not-sticky). In order to reduce the time and probability of this downtime, you could start the service in foreground (see `startForeground(int id, Notification notification)`). – Trinimon Aug 11 '13 at 18:41
  • but I do want to destroy the service when screen turns off, and then I'm not able to wake it up again when screen turns on since there is no registeration to the screen broadcast anymore. – adl Aug 12 '13 at 08:24
  • 1
    I think the service won't consume too much battery, if no code is running inside of it (i.e. there are no running threads) and if it is not called constantly again and again (in my app it's less than 1% and I've registered several receivers and observers). May be I was a bit unprecise above, when I wrote *stop service* 'cause what I meant was: stop all threads or registered event handlers that might cause some CPU load. – Trinimon Aug 12 '13 at 09:13
  • 1
    I also think that the service should not be consuming much battery in case no code is running in it, but people still complaining that it does eats their battery event though I only have receivers for screen on, screen off, and phone_state – adl Aug 12 '13 at 10:54
  • If the service/app consumes a lot of CPU/battery this could be an indicator for some erroneous code. I encountered some situations were even wide spread apps (OSMAnd, Facebook, ....) burned the battery down :) It's often hard to identify which program causes trouble in particular if it happens rather rarely. So I wouldn't be surprised, if you are blamed for something that is not connected to your app. Anyhow, you could try to analyse the system load with *OS Monitor* or *SystemPanel*. – Trinimon Aug 12 '13 at 11:38
  • Hey, I have a question about keeping my service alive: http://stackoverflow.com/questions/35168769/start-in-foreground – Ruchir Baronia Feb 03 '16 at 04:55