Ожидание асинхронного обратного вызова в IntentService Android
у меня есть IntentService
который запускает асинхронную задачу в другом классе и должен ждать результата.
проблема в том, что IntentService
закончит, как только onHandleIntent(...)
метод закончил работать, не так ли?
это означает, как правило,IntentService
немедленно завершит работу после запуска асинхронной задачи и больше не будет получать результаты.
public class MyIntentService extends IntentService implements MyCallback {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected final void onHandleIntent(Intent intent) {
MyOtherClass.runAsynchronousTask(this);
}
}
public interface MyCallback {
public void onReceiveResults(Object object);
}
public class MyOtherClass {
public void runAsynchronousTask(MyCallback callback) {
new Thread() {
public void run() {
// do some long-running work
callback.onReceiveResults(...);
}
}.start();
}
}
как я могу сделать код выше работает? Я уже попробовал положить Thread.sleep(15000)
(произвольной длительности) в onHandleIntent(...)
после запуска задач. Это должно сработать.
но это определенно не кажется чистым решением. Может быть, с этим даже есть какие-то серьезные проблемы.
решение получше?
6 ответов
использовать стандартный Service
класс вместо IntentService
, запустите асинхронную задачу из onStartCommand()
обратный вызов, и уничтожить Service
при получении обратного вызова завершения.
проблема с тем, что было бы правильно обрабатывать уничтожение Service
в случае одновременного выполнения задач в результате Service
запускается снова, когда он уже запущен. Если вам нужно обработать это дело, вам может потребоваться настроить счетчик или набор обратные вызовы, и уничтожить Service
только когда они все завершены.
Я согласен с corsair992, что обычно вам не нужно делать асинхронные вызовы из IntentService, потому что IntentService уже выполняет свою работу в рабочем потоке. Однако, если вы должны сделайте так, чтобы вы могли использовать CountDownLatch за.
public class MyIntentService extends IntentService implements MyCallback {
private CountDownLatch doneSignal = new CountDownLatch(1);
public MyIntentService() {
super("MyIntentService");
}
@Override
protected final void onHandleIntent(Intent intent) {
MyOtherClass.runAsynchronousTask(this);
doneSignal.await();
}
}
@Override
public void onReceiveResults(Object object) {
doneSignal.countDown();
}
public interface MyCallback {
public void onReceiveResults(Object object);
}
public class MyOtherClass {
public void runAsynchronousTask(MyCallback callback) {
new Thread() {
public void run() {
// do some long-running work
callback.onReceiveResults(...);
}
}.start();
}
}
Если вы все еще ищете способы использования службы Intent для асинхронного обратного вызова, вы можете подождать и уведомить поток следующим образом,
private Object object = new Object();
@Override
protected void onHandleIntent(Intent intent) {
// Make API which return async calback.
// Acquire wait so that the intent service thread will wait for some one to release lock.
synchronized (object) {
try {
object.wait(30000); // If you want a timed wait or else you can just use object.wait()
} catch (InterruptedException e) {
Log.e("Message", "Interrupted Exception while getting lock" + e.getMessage());
}
}
}
// Let say this is the callback being invoked
private class Callback {
public void complete() {
// Do whatever operation you want
// Releases the lock so that intent service thread is unblocked.
synchronized (object) {
object.notifyAll();
}
}
}
мой любимый вариант-выставить два похожих метода, например:
public List<Dog> getDogsSync();
public void getDogsAsync(DogCallback dogCallback);
тогда реализация может быть следующая:
public List<Dog> getDogsSync() {
return database.getDogs();
}
public void getDogsAsync(DogCallback dogCallback) {
new AsyncTask<Void, Void, List<Dog>>() {
@Override
protected List<Dog> doInBackground(Void... params) {
return getDogsSync();
}
@Override
protected void onPostExecute(List<Dog> dogs) {
dogCallback.success(dogs);
}
}.execute();
}
затем в IntentService
можно назвать getDogsSync()
потому что он уже находится в фоновом потоке.
вы обречены, не меняясь MyOtherClass
.
С изменением этого класса у вас есть два варианта:
- сделать синхронный вызов.
IntentService
уже порождает фонThread
для вас. - вернуть вновь созданный
Thread
наrunAsynchronousTask()
и звонокjoin()
на нем.
Я согласен, вероятно, имеет смысл использовать Service
напрямую, а не IntentService
, а если вы используете гуавы можно реализовать AbstractFuture
в качестве обработчика обратного вызова, который позволяет вам удобно игнорировать детали синхронизации:
public class CallbackFuture extends AbstractFuture<Object> implements MyCallback {
@Override
public void onReceiveResults(Object object) {
set(object);
}
// AbstractFuture also defines `setException` which you can use in your error
// handler if your callback interface supports it
@Override
public void onError(Throwable e) {
setException(e);
}
}
AbstractFuture
определяет get()
, который блокирует до тех пор, пока set()
или setException()
методы вызываются, и возвращает значение или вызывает исключение, соответственно.
иначе onHandleIntent
становится:
@Override
protected final void onHandleIntent(Intent intent) {
CallbackFuture future = new CallbackFuture();
MyOtherClass.runAsynchronousTask(future);
try {
Object result = future.get();
// handle result
} catch (Throwable t) {
// handle error
}
}