Долго работающий сервис потребляет много батареи

Я разработал приложение, и некоторые люди жалуются, что оно потребляет слишком много энергии, это второй по потреблению процесс после экрана. Однако в некоторых устройствах он не потребляет столько батареи.

Вся работа, которую выполняет мое приложение, находится в службе. Служба липкая и работает все время (система Android может убить ее, когда у нее мало ресурсов, или приостановить ее, когда устройство переходит в спящий режим), она слушает акселерометр, пока экран включен, это не служба переднего плана и не держит wakelock.

Может кто-нибудь сказать мне, почему это занимает много батареи? И почему это происходит только на некоторых устройствах?

Вот соответствующий код:

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...
    }
}

person adl    schedule 11.08.2013    source источник
comment
Я бы предложил отладить, сколько раз вызываются эти BroadcastReceivers и сколько ресурсов они получают.   -  person abbath    schedule 11.08.2013
comment
@abbath, как я могу отладить, сколько ресурсов они получают?   -  person adl    schedule 11.08.2013
comment
Взгляните на этот инструмент для измерения использования ЦП/батареи и т. д. — littleeye.co.   -  person Deepak Bala    schedule 11.08.2013
comment
@DeepakBala, ваша ссылка, к сожалению, не работает.   -  person Gregor Ažbe    schedule 25.04.2019


Ответы (1)


Постоянный запуск сервисов может быть дорогостоящим с точки зрения процессора и батареи — это один из недостатков сервиса. В частности, если вы включаете потоки, вызывающие некоторую нагрузку на ЦП. Есть несколько вариантов на выбор, в зависимости от требований вашего приложения:

  1. Если результат вашей службы актуален только в то время, когда пользователь может его использовать, вы можете подумать об остановке и запуске службы на экране при включении и выключении событий или, по крайней мере, о запуске и остановке включенных потоков/обработчиков. Это можно сделать с помощью 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. Если вам нужно регулярно обрабатывать/запускать данные/код, вы можете подумать об использовании 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, ...)

Так что есть некоторые альтернативы. Глядя на ваш код, я вижу, что вы что-то делаете с датчиками. Вам нужна информация датчика, если экран выключен? Или какие события могут вызвать начало и конец вашей службы?

Если вам действительно нужны результаты все время (например, для GPS-трекера), у вас, вероятно, нет другого выбора, кроме оптимизации вашего кода. Может быть имеет смысл остановить службу, если батарея разряжена (см. это для более подробной информации).

Удачи!

person Trinimon    schedule 11.08.2013
comment
Спасибо, я хочу использовать вариант 1, но поскольку я регистрируюсь для трансляции программно в службе, время жизни трансляции зависит от времени жизни приложения, и если я остановлю службу, когда экран выключится, тогда время жизни приложения закончится, и регистрация трансляции будет отменена. и я не смогу запустить службу, когда загорится экран. - person adl; 11.08.2013
comment
Что ж, если вы запустите свою службу как липкую, экземпляр Service, скорее всего, не будет уничтожен, пока вы не вызовете stopSelf(), поэтому вы можете зарегистрировать приемники экрана здесь. Есть одно исключение: если системе не хватает памяти, служба может быть уничтожена. Из-за липкого флага он будет перезапущен, как только в системе снова будет достаточно памяти (см. stackoverflow.com/questions/9093271/). Чтобы сократить время и вероятность этого простоя, вы можете запустить службу на переднем плане (см. startForeground(int id, Notification notification)). - person Trinimon; 11.08.2013
comment
но я хочу уничтожить службу, когда экран выключается, а затем я не могу снова разбудить ее, когда экран включается, поскольку больше нет регистрации для трансляции экрана. - person adl; 12.08.2013
comment
Я думаю, что сервис не будет потреблять слишком много батареи, если внутри него не выполняется код (т.е. нет запущенных потоков) и если он не вызывается постоянно снова и снова (в моем приложении это менее 1%, и я мы зарегистрировали несколько приемников и наблюдателей). Возможно, я был немного неточен выше, когда написал stop service, потому что я имел в виду: остановить все потоки или зарегистрированные обработчики событий, которые могут вызвать некоторую нагрузку на ЦП. - person Trinimon; 12.08.2013
comment
Я также думаю, что служба не должна потреблять много батареи, если в ней не работает код, но люди все еще жалуются, что она съедает их событие батареи, хотя у меня есть только приемники для включения экрана, выключения экрана и phone_state. - person adl; 12.08.2013
comment
Если служба/приложение потребляют много ресурсов ЦП/батареи, это может быть признаком ошибочного кода. Я сталкивался с некоторыми ситуациями, когда даже широко распространенные приложения (OSMAnd, Facebook, ....) сжигали батарею :) Часто трудно определить, какая программа вызывает проблемы, если это происходит довольно редко. Поэтому я не удивлюсь, если вас обвинят в чем-то, что не связано с вашим приложением. В любом случае вы можете попытаться проанализировать загрузку системы с помощью OS Monitor или SystemPanel. - person Trinimon; 12.08.2013
comment
Привет, у меня есть вопрос о том, как сохранить мой сервис: stackoverflow.com/questions/35168769/start-in-foreground< /а> - person Ruchir Baronia; 03.02.2016