【问题标题】:Best way to guarantee a callback is always executed asynchronously?保证回调始终异步执行的最佳方法?
【发布时间】:2013-06-27 19:24:58
【问题描述】:

我正在使用一个 Android 应用程序,它必须发出服务器请求,然后在请求完成后执行操作。下面是一些伪代码来帮助解释这种情况:

makeRequest(new SomeTask{
    onDone() {
        // Do actionB with queue
    }
});

// Do actionA with queue.  Must be execute first!!

下面是makeRequest的伪代码实现:

makeRequest(SomeTask task) {
    if(canDoOptimization) { // if true, don't need to make request

        // It's a bad idea to execute this immediately.
        // Wish I could wait until the current thread of execution was done...
        task.onDone();
        return;
    }

    asyncTask = new AsyncTask<SomeTask, Void, Void>() {
        doInBackground(SomeTask... task) {
            // Make server request...

            task.onDone();
        }
    }
    asyncTask.execute(task);
}

通常actionA 按预期发生在actionB 之前,但在我们可以避免网络请求的情况下,SomeTask.execute 会立即被调用。这会导致actionB 发生在之前 actionA,这很糟糕。有什么办法可以保证不会发生这种情况?

我在 javascript 中多次遇到这种情况。在这些情况下,我会用setTimeoutsetImmediate 包装SomeTask.execute 调用以保持正确的异步语义。

为了清楚起见,下面是 JavaScript 中相同错误的示例:https://gist.github.com/xavi-/5882483

知道我应该在 Java/Android 中做什么吗?

【问题讨论】:

  • 为什么不能在makeRequest之前调用action A?
  • 这是一种可能性,但需要大量重构。另外,我希望有一个通用的解决方案,所以我无论如何都不必注意这个问题。
  • 设置超时似乎是一个非常hacky的解决方案
  • 对,在 Java 中可能是这样。但在 JS 中,我觉得这是 setTimeout 的标准用例。这很常见,以至于 Node.js 提供了一个更优化的版本,称为 process.nextTick

标签: java android concurrency


【解决方案1】:

欢迎来到同步世界。互斥或锁定对象通常用于此目的。 Is there a Mutex in Java?

您的 B 任务应该等待任务 A 在完成时发出信号的互斥锁。这将确保 A 任务在 B 之前完成的正确执行顺序。

【讨论】:

    【解决方案2】:

    始终将task.onDone() 放在AsyncTask 中,即使它不必提出请求。

    makeRequest(SomeTask task) {
    
        asyncTask = new AsyncTask<SomeTask, Void, Void>() {
            void doInBackground(SomeTask... task) {
    
                if(canDoOptimization) { // if true, don't need to make request
    
                    // It's a bad idea to execute this immediately.
                    // Wish I could wait until the current thread of was done...
                    task.onDone();
                    return;
                } else {
                    // Make server request...
    
                    task.onDone();
                }
            }
        }
        asyncTask.execute(task);
    }
    

    【讨论】:

    • 这很有意义。不幸的是,我过度简化了我的例子。 canDoOptimization 检查和实际的网络调用发生在不同的地方。 SomeTask 对象被传递了很多。也许我可以将task.onDone() 调用包装在一个虚拟AsyncTask 中?
    • 一个虚拟的 AsyncTask 值得一试。我实际上不是 Android 专家,我只是在黑暗中拍摄 :)
    【解决方案3】:

    为什么不能直接切换顺序?

    // Do actionA with queue.  Must be execute first!!
    
    makeRequest(new SomeTask{
    onDone() {
        // Do actionB with queue
    });
    

    如果 actionA 也是异步的并且在单独的 AsyncTask 上执行,您可以在 actionA 的 AsyncTasks 的 onPostExecute() 方法上调用 makeRequest(...)。

    顺便说一句,从 Android Honeycomb 版本开始,AsyncTask 运行在同一个线程上,这意味着如果您有多个任务,它们可以相互阻塞。 通过指定 AsyncTsak 应在线程池中运行来解决此问题:

    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }
    else {
      asyncTask.execute();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-09
      • 1970-01-01
      • 2016-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-24
      • 1970-01-01
      相关资源
      最近更新 更多