Подтвердить что ты не робот

Деятельность с длительным обслуживанием в фоновом режиме, которая не будет убита

На Android у меня есть Activity, называемый FirstActivity, который запускает Service с именем MyService, чтобы делать сетевые материалы в фоновом режиме. Activity и Service постоянно взаимодействуют друг с другом, вызывая методы.

Теперь, когда пользователь переходит от FirstActivity в SecondActivity, фоновая служба не должна быть убита или воссоздана, но сохранена в памяти и передана SecondActivity, которая теперь будет той, которая связывается с этой службой.

Другими словами, Service должен работать до тех пор, пока выполняется один из двух Activity, и он не должен останавливаться, пока пользователь перемещается между двумя Activity s.

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

(Если бы не было возможности предотвратить время от времени от убийства и повторного создания службы, мне понадобится способ изящного восстановления полного состояния службы.)

Подводя итог, Service должен иметь такую ​​же продолжительность жизни, как два Activity "вместе". Он должен начинаться с первого из них и останавливаться не раньше, чем обе они были уничтожены.

Итак, правильный ли код для этой установки и целей?

public class MyService extends Service {

    public class LocalBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }

    ...

}

public class FirstActivity extends Activity {

    private MyService mMyService;

    private ServiceConnection mMainServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            MyService mainService = ((LocalBinder) service).getService();
            mMyService = mainService;
            mMyService.setCallback(FirstActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            mMyService = null;
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        startService(new Intent(FirstActivity.this, MyService.class));
    }

    @Override
    protected void onResume() {
        super.onResume();
        bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mMainServiceConnection != null) {
            unbindService(mMainServiceConnection);
        }

        if (mMyService != null) {
            mMyService.setCallback(null);
        }

        if (!isUserMovingToSecondActivity) {
            stopService(new Intent(FirstActivity.this, MyService.class));
        }
    }

    @Override
    public void onBackPressed() {
        stopService(new Intent(FirstActivity.this, MyService.class));
        super.onBackPressed();
    }

    ...

}

public class SecondActivity extends Activity {

    private MyService mMyService;

    private ServiceConnection mMainServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            MyService mainService = ((LocalBinder) service).getService();
            mMyService = mainService;
            mMyService.setCallback(SecondActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            mMyService = null;
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mMainServiceConnection != null) {
            unbindService(mMainServiceConnection);
        }
    }

    @Override
    protected void onDestroy() {
        ...
        stopService(new Intent(SecondActivity.this, MyService.class));
    }

    ...

}

Это лучший способ гарантировать долговременную службу на фоне Activity, которая не будет убита или воссоздана?

Как насчет Context.BIND_AUTO_CREATE? Правильно ли здесь установить этот флаг? Что насчет Context.BIND_ADJUST_WITH_ACTIVITY и Context.BIND_WAIVE_PRIORITY - мне нужны эти?

4b9b3361

Ответ 1

(Большое спасибо @corsair992 за полезные подсказки!)


Если действия всегда вызывают в этом порядке (т.е. FirstActivity запускает SecondActivity и никогда не будет наоборот, тогда вы должны, в основном, попытаться "привязать" жизненный цикл службы к FirstActivity жизненному циклу.

В целом (см. оговорки позже), это означает:

  • Вызов startService() в FirstActivity.onCreate().
  • Вызов stopService() в FirstActivity.onDestroy().
  • Вызовите bindService()/unbindService() в методах onStart()/onStop() обеих Деяний (чтобы получить доступ к объекту Binder и иметь возможность вызывать на нем методы).

Служба, запущенная таким образом, будет жива до тех пор, пока stopService() не будет называться и, каждый клиент отсоединяет, см. Управление жизненным циклом службы

Эти два пути не являются полностью отдельными. То есть вы можете привязываться к сервис, который уже был запущен с помощью startService(). (...) В таких случаях stopService() или stopSelf() фактически не останавливает службу до тех пор, пока все клиенты отвязать.

и

Когда последний клиент отсоединяется от службы, система уничтожает (если служба также не была запущена службой startService()).

С помощью этой базовой стратегии Служба будет работать до тех пор, пока вокруг будет FirstActivity (т.е. она не будет уничтожена). Тем не менее, остается еще важный момент: в случае изменения конфигурации (например, вращения экрана), которое не обрабатывается явно, это приведет к перезагрузке активности, и служба будет уничтожена (поскольку мы вызываем stopService() в onDestroy()).

Чтобы предотвратить это, вы можете проверить isChangingConfigurations() до фактической остановки службы (поскольку обратный вызов onDestroy(), возникающий по этой причине, означает, что хотя этот конкретный экземпляр Деятельности уничтожается, она будет воссоздана впоследствии.

Следовательно, полное решение будет выглядеть примерно так:

public class FirstActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, MyService.class));
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() { ... }

    @Override
    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        unbindService(mServiceConnection);
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        if (!isChangingConfigurations())
            stopService(new Intent(this, MyService.class));

        super.onDestroy();
    }

Пока SecondActivity будет только реализовывать методы onStart()/onStop() (таким же образом).


Несколько примечаний о вашей конкретной реализации:

  • Не нужно переопределять onBackPressed(), так как если действие будет уничтожено, будут вызваны необходимые методы жизненного цикла (плюс, он может быть закончен без нажатия кнопки "Назад", например, если вы вызываете finish() на нем).
  • Остановка службы в onDestroy() вместо onPause() избавляет вас от необходимости проверять isUserMovingToSecondActivity.