我有一个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(...)
(任意持续时间)。它似乎工作。
但它绝对不是一个干净的解决方案。也许甚至有一些严重的问题。
有更好的解决方案吗?
答案 0 :(得分:18)
使用标准Service
类而不是IntentService
,从onStartCommand()
回调启动异步任务,并在收到完成回调时销毁Service
。
这个问题是在Service
已经运行时再次启动Service
的情况下,在同时运行任务的情况下正确处理Service
的销毁。如果你需要处理这种情况,那么你可能需要设置一个正在运行的计数器或一组回调,并且只有当它们全部完成时才销毁{{1}}。
答案 1 :(得分:10)
我同意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();
}
}
答案 2 :(得分:6)
如果您仍在寻找使用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();
}
}
}
答案 3 :(得分:2)
我最喜欢的选择是公开两种类似的方法,例如:
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()
,因为它已经在后台主题中。
答案 4 :(得分:1)
你注定没有改变MyOtherClass
。
通过更改该课程,您有两种选择:
IntentService
已经为您生成了背景Thread
。Thread
中返回新创建的runAsynchronousTask()
并在其上调用join()
。答案 5 :(得分:0)
我同意,直接使用Service
而不是IntentService
更有意义,但如果您使用Angular 2's router documentation,则可以实施AbstractFuture
作为回调handler,它可以让你方便地忽略同步的细节:
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
}
}