Избегайте обратного вызова службы, когда действие закрывается и вновь открывается - PullRequest
1 голос
/ 11 августа 2011

У меня есть LocalService, который предоставляет Binder с некоторыми API. Я создаю Service Listener, вот так:

if (dataServiceListener == null) {
    dataServiceListener = new DataServiceListener();
    mainActivity.getApplicationContext().bindService
        (new Intent(mainActivity, LocalService.class),
        dataServiceListener.svcConn, mainActivity.BIND_AUTO_CREATE);
}

После того, как я вызываю метод, который предоставляет Binder в dataServiceListener, я получаю ответ в методе dataServiceListener onResult(). До этого момента никаких проблем не возникало, все работает. Некоторая проблема возникает, когда я закрываю Activity, которая ожидает обратного вызова Service Listener, и немедленно снова открываю ее. Несмотря на то, что я восстанавливаю dataServiceListener в onCreate(), я получаю два обратных вызова вместо одного: старый от уничтоженной Деятельности и последний (правый); Таким образом, результаты смешиваются в пользовательском интерфейсе. Есть ли способ сообщить службе или прослушивателю службы, что после завершения действия необходимо избегать обратных вызовов. Или, может быть, даже уничтожить объекты ServiceListener.

Я думаю, что это проблема, которую Марк Л. Мерфи (Commonsware) описал в «Руководстве занятого программиста по разработке Android»:

Самым большим подвохом является то, что задание отвлекает слушателей, когда оно выполнено.

Как я могу это сделать? Есть ли способ избавиться от бесполезных слушателей, когда действие заканчивается?

Спасибо!

Ответы [ 5 ]

8 голосов
/ 05 ноября 2012

У меня была такая же проблема.Я работал в удаленной службе, используя AIDL.У меня возникла эта проблема, когда я пытаюсь отменить регистрацию моих слушателей, используя метод remove из ArrayList Collection внутри цикла foreach, потому что я не использовал asBinder в сравнении.В поисках решения я обнаружил класс RemoteCallbackList в Android API.Этот класс делает именно то, что мне было нужно, и то, что я думаю, вы должны сделать простым способом, взяв все возможности для тяжелой работы, связанной с этой задачей.

Из API Android:

Чтобы использовать этот класс, просто создайте один экземпляр вместе с вашим сервисом и вызовите его методы register (E) и unregister (E) как клиентский регистр и отмените регистрацию в вашем сервисе.Чтобы перезвонить зарегистрированным клиентам, используйте beginBroadcast (), getBroadcastItem (int) и finishBroadcast ().

Пример трансляции:

int i = callbacks.beginBroadcast();
while (i > 0) {
    i--;
    try {
        callbacks.getBroadcastItem(i).somethingHappened();
    } catch (RemoteException e) {
    // The RemoteCallbackList will take care of removing
    // the dead object for us.
   }
}
callbacks.finishBroadcast();
4 голосов
/ 11 августа 2011

Код, который вы показываете, предназначен для привязки к услуге. Вы не показываете, где вы регистрируете слушателя с этим сервисом. Вы, очевидно, основаны на вашем вопросе и вашей ссылке на метод onResult(). Учитывая природу вашей проблемы, я собираюсь догадаться, что вы делаете:

  1. Привязка к услуге в onCreate()
  2. В onServiceConnected() вы вызываете какой-то setListener() метод для Binder

В этом случае, если мы проигнорируем изменения конфигурации, правильный способ раскрутки будет состоять в том, чтобы в onDestroy() вызвать некоторый метод removeListener() для Binder, а затем вызвать unbindService().

Изменения конфигурации, особенно в мире перед фрагментом, усложняют это. Это причина, по которой этот пример проекта (и сопутствующий материал в книге ) настолько неприглядный. Связывание является раздражительным - если вы открепляетесь от старого действия, и ничто иное не удерживает службу, служба отключится до того, как новое действие получит возможность связать. Привязка также является состоянием - вы не можете просто отменить привязку, чтобы не пропустить вещи.

Итак, рецепт становится:

  1. Привязка к услуге в onCreate() с использованием Application Context
  2. В onServiceConnected() вызовите метод setListener() для Binder
  3. В onRetainNonConfigurationInstance() обратите внимание на то, что вы претерпеваете изменение конфигурации, и верните немного Object, в котором есть Binder, Listener и все остальное в вашем состоянии
  4. В onCreate() используйте getLastNonConfigurationInstance() - если это null, продолжайте как обычно, но если это не null, держитесь за эти Binder и Listener и не повторяйте привязать и перерегистрировать слушателя
  5. В onDestroy(), если флаг из шага # 3 выше false (т. Е. Мы не претерпеваем изменения конфигурации), вызовите некоторый метод removeListener() для Binder, затем позвоните unbindService().

Использование фрагментов с setRetainInstance(true), вероятно, может несколько упростить это, хотя я еще не работал с образцом для этого.

1 голос
/ 11 августа 2011

Ваша деятельность должна зарегистрироваться / отменить свою регистрацию в качестве слушателя.Вам нужно использовать правильные методы обратного вызова жизненного цикла, а не onBackPressed().Зарегистрироваться onStart(), отменить регистрацию onStop().Один из способов сделать это - сделать слушателя статическим членом вашего сервиса и предоставить статические методы регистрации / отмены регистрации.Затем назовите тех из вашей деятельности, как это необходимо.

1 голос
/ 11 августа 2011

У меня тоже была эта проблема.Вам нужно освободить все ресурсы, слушатели, потоки из службы, когда она завершится.

0 голосов
/ 13 сентября 2011

Я наконец решил проблему (и нет, я так долго над ней не работал: D).

Обратный вызов слушателя был сделан до того, как Fragment 'onDestroy был вызван. Таким образом, логическое значение "dontupdate" никогда не было установлено в false. Переопределение onBackPressed в основном действии решило проблему, так как я вызвал метод destroy() для каждого фрагмента, который заботится о том, чтобы установить логическое значение в false.

...