在Android的IntentService中等待异步回调

我有一个IntentService ,它在另一个类中启动异步任务,然后应该等待结果。

问题是一旦onHandleIntent(...)方法运行完毕, IntentService就会完成,对吧?

这意味着,通常, 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(...) 。 它似乎工作。

但它绝对不是一个干净的解决方案。 也许甚至有一些严重的问题。

更好的解决方案?

使用标准Service类而不是IntentService ,从onStartCommand()回调启动异步任务,并在收到完成回调时销毁Service

这样做的问题是,如果在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 Service进行异步回调的方法,您可以在线程上等待并通知如下,

 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 getDogsSync(); public void getDogsAsync(DogCallback dogCallback); 

然后执行如下:

 public List getDogsSync() { return database.getDogs(); } public void getDogsAsync(DogCallback dogCallback) { new AsyncTask>() { @Override protected List doInBackground(Void... params) { return getDogsSync(); } @Override protected void onPostExecute(List dogs) { dogCallback.success(dogs); } }.execute(); } 

然后在您的IntentService您可以调用getDogsSync()因为它已经在后台线程上。

你注定没有改变MyOtherClass

通过更改该课程,您有两种选择:

  1. 进行同步通话。 IntentService已经为您生成了一个后台Thread
  2. runAsynchronousTask()返回新创建的Thread并在其上调用join()

我同意,直接使用Service而不是IntentService可能更有意义,但是如果你使用的是Guava ,你可以将AbstractFuture实现为你的回调处理程序,它可以让你方便地忽略同步的细节:

 public class CallbackFuture extends AbstractFuture 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()方法,并分别返回一个值或引发一个exception。

然后你的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 } }