Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long running service consumes a lot of battery

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...
    }
}
like image 458
adl Avatar asked Aug 11 '13 09:08

adl


1 Answers

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!

like image 186
Trinimon Avatar answered Oct 06 '22 01:10

Trinimon