【问题标题】:Waiting for asynchronous callback in Android's IntentService在 Android 的 IntentService 中等待异步回调
【发布时间】:2016-08-03 06:49:37
【问题描述】:

我有一个 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();
    }

}

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

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

有更好的解决方案吗?

【问题讨论】:

  • 是什么阻止您制作同一事物的同步版本?
  • 你可以使用 bindservice 。 developer.android.com/guide/components/bound-services.html 这对我有很大帮助。
  • @ianhanniballake:MyOtherClass 实际上是我不想更改的第三方组件的一部分。该类中的方法始终通过回调而不是同步传递其结果。
  • @mcd:绑定服务仅适用于Service 向另一个应用程序组件(通常是Activity)报告的用例,不是吗?
  • 是的,但是您可以使用服务类代替抱歉没有正确阅读您的问题

标签: java android asynchronous intentservice


【解决方案1】:

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

这样做的问题是正确处理Service 的破坏,因为Service 在它已经运行的同时又被重新启动了。如果您需要处理这种情况,那么您可能需要设置一个正在运行的计数器或一组回调,并仅在它们全部完成后销毁Service

【讨论】:

  • 同意。 IntentService 不是为这种情况设计的。
  • @CommonsWare 为什么 GCM 的 google sample project 在 IntentService 中设置 http 调用(sendRegistrationToServer 方法)。在发送 gcm 令牌时,肯定有 99.9% 的人希望等待服务器的响应,对吧?然而,在 IntentService 中等待来自 http 调用的回调并不是 IntentService 的设计目的。
  • @AdamJohns:“然而,在 IntentService 中等待来自 http 调用的回调并不是 IntentService 的设计目的”——在这种情况下,我不知道您所说的“回调”是什么意思。对于IntentService 来说,进行 HTTP I/O 是一件非常合理的事情,只要它同步进行。如果您在这方面还有其他顾虑,我建议您单独提出 Stack Overflow 问题。
  • @CommonsWare 当我说回调时,我的意思是我想使用 gcm 令牌向我的服务器发送一个 http 请求,并且“回调”将从服务器获取响应。在这里查看我的问题:stackoverflow.com/q/36501842/1438339
【解决方案2】:

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

}

【讨论】:

  • 这有什么缺点吗?似乎仍然是一个非常直接的选择?
  • 为了提高生产质量,您可能希望使用新线程以外的其他东西,也许是 Executor,但使用 CountDownLatch 的基本思想是非常基本的。
【解决方案3】:

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

【讨论】:

  • 你能不能多做点这个
【解决方案4】:

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

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(),因为它已经在后台线程上。

【讨论】:

  • 谢谢,这正是我为解决这个问题所做的:)
【解决方案5】:

不改MyOtherClass你就注定了。

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

  1. 进行同步调用。 IntentService 已经为你生成了一个背景 Thread
  2. runAsynchronousTask()中返回新创建的Thread并调用join()就可以了。

【讨论】:

    【解决方案6】:

    我同意,直接使用Service 可能比IntentService 更有意义,但是如果您使用Guava,您可以实现一个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
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2021-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 2018-09-22
      • 2017-06-29
      • 2019-11-15
      • 1970-01-01
      相关资源
      最近更新 更多