Java 在 Android 的 IntentService 中等待异步回调

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21964375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:41:37  来源:igfitidea点击:

Waiting for asynchronous callback in Android's IntentService

javaandroidasynchronousintentservice

提问by caw

I have an IntentServicethat starts an asynchronous task in another class and should then be waiting for the result.

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

The problem is that the IntentServicewill finish as soon as the onHandleIntent(...)method has finished running, right?

问题是方法一运行IntentService完成就会onHandleIntent(...)结束,对吗?

That means, normally, the IntentServicewill immediately shut down after starting the asynchronous task and will not be there anymore to receive the results.

这意味着,通常情况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();
    }

}

How can I make the snippet above work? I've already tried putting Thread.sleep(15000)(arbitrary duration) in onHandleIntent(...)after starting the task. Itseems to work.

我怎样才能使上面的代码片段工作?我已经尝试在开始任务后放入Thread.sleep(15000)(任意持续时间)onHandleIntent(...)。它似乎工作。

But it definitely doesn't seem to be clean solution. Maybe there are even some serious problems with that.

但这绝对不是干净的解决方案。也许这甚至存在一些严重的问题。

Any better solution?

有什么更好的解决办法吗?

采纳答案by corsair992

Use the standard Serviceclass instead of IntentService, start your asynchronous task from the onStartCommand()callback, and destroy the Servicewhen you receive the completion callback.

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

The issue with that would be to correctly handle the destruction of the Servicein the case of concurrently running tasks as a result of the Servicebeing started again while it was already running. If you need to handle this case, then you might need to set up a running counter or a set of callbacks, and destroy the Serviceonly when they are all completed.

这样做的问题是Service在并发运行任务的情况下正确处理销毁,因为Service它已经在运行时再次启动。如果您需要处理这种情况,那么您可能需要设置一个运行计数器或一组回调,并Service在它们全部完成时销毁它们。

回答by flx

You are doomed without changing MyOtherClass.

你注定不改MyOtherClass

With changing that class you have two options:

通过更改该类,您有两个选择:

  1. Make an synchronous call. IntentServiceis already spawning a background Threadfor you.
  2. Return the newly created Threadin runAsynchronousTask()and call join()on it.
  1. 进行同步调用。IntentService已经Thread为您生成背景。
  2. 返回新创建的ThreadinrunAsynchronousTask()并调用join()它。

回答by keerthy

If you are still looking for ways to use Intent Service for asynchronous callback, you can have a wait and notify on thread as follows,

如果您仍在寻找使用 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();
        }   
    }
}

回答by Matt Logan

My favorite option is to expose two similar methods, for example:

我最喜欢的选择是公开两个类似的方法,例如:

public List<Dog> getDogsSync();
public void getDogsAsync(DogCallback dogCallback);

Then the implementation could be as follows:

那么实现可能如下:

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();
}

Then in your IntentServiceyou can call getDogsSync()because it's already on a background thread.

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

回答by Matt Accola

I agree with corsair992 that typically you should not have to make asynchronous calls from an IntentService because IntentService already does its work on a worker thread. However, if you mustdo so you can use CountDownLatch.

我同意 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();
    }

}

回答by Tom Lubitz

I agree, it probably makes more sense to use Servicedirectly rather than IntentService, but if you are using Guava, you can implement an AbstractFutureas your callback handler, which lets you conveniently ignore the details of synchronization:

我同意,Service直接使用可能比使用更有意义IntentService,但是如果您使用的是Guava,您可以实现 anAbstractFuture作为您的回调处理程序,这样您就可以方便地忽略同步的细节:

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);
    }
}

AbstractFuturedefines get()which blocks until the set()or setException()methods are called, and returns a value or raises an exception, respectively.

AbstractFuture分别定义在调用或方法get()之前哪些块,并分别返回值或引发异常。set()setException()

Then your onHandleIntentbecomes:

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