Деятельность с длительным обслуживанием в фоновом режиме, которая не будет убита
На 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
- мне нужны эти?
Ответы
Ответ 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
.