2014-09-26 95 views
8

我有一系列的异步操作链的一系列异步调用

private void doSomething(){ 
    get("/something", new Callback(){ 
    void onComplete(String data){ 
     updateUi(something, data);   
     doSomethingElse(); 
    } 
    }); 
} 

private void doSomethingElse(){ 
    get("/something/else", new Callback(){ 
    void onComplete(String data){ 
     updateUi(somethingElse, data); 
     doYetAnotherThing(); 
    } 
    }); 
} 

private void doYetAnotherThing(){ 
    get("/yet/another/thing", new Callback(){ 
    void onComplete(String data){ 
     updateUi(yetAnotherThing, data); 
     allDone(); 
    } 
    }); 
} 

这从几个问题遭受的:

  1. 不能再使用任何其他地方的回调,因为每个固定绑定到“下一步“
  2. 重新排序操作或插入另一个操作是非直观的,并涉及到所有地方跳跃。

我已经看过下面的选项,以减轻这一点:

  1. ExecuterService#invokeAll - 我看不出这个解决方案可以无阻塞地使用。
  2. RxJava - 如果可以的话,我宁愿避免在应用程序中发生这种范式转变!
  3. 番石榴的ListenableFutures及其transform方法。我看到这在互联网螺母周围的少数地方提到,我真的不知道这将如何解决我的问题。

所以,问题是:在Java中链接一系列异步调用的好模式是什么?因为我需要一个适用于Android应用程序的解决方案,所以需要使用Java 7。

回答

2

肯定有一些猜测,涉及到您遇到此问题的实际意图和用例。此外,还不完全清楚something,somethingElseyetAnotherThing是哪些(它们来自哪里以及它们应该去哪里)。

然而,根据您提供的信息,并作为除了(或者说扩展或泛化)的answer by slartidan:你勾勒这些虚拟调用之间的差异,似乎有

  • 的传递到get方法
  • Callback那个叫
  • 哪种方法接着执行String参数

你可以分析出这些部分:String参数和Callback可以作为参数来创建一个Callable的一般方法进行传递。调用的顺序可以简单地通过将这些Callable对象到一个列表中,以适当的顺序进行定义,并与单线程执行服务执行它们。

正如你在这个例子中的main方法看,调用序列然后可以被配置相当容易:

import java.util.Arrays; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 


public class ChainedAsyncTest { 

    public static void main(String[] args) throws InterruptedException { 
     ChainedAsyncTest t = new ChainedAsyncTest(); 
     ExecutorService e = Executors.newFixedThreadPool(1); 
     e.invokeAll(Arrays.asList(
      t.call("/something", t.somethingCallback), 
      t.call("/something/else", t.somethingElseCallback), 
      t.call("/yet/another/thing", t.yetAnotherThingCallback), 
      t.allDone())); 
    } 

    private Callback somethingCallback = new Callback() { 
     @Override 
     public void onComplete(String data) { 
      updateUi("something", data); 
     } 
    }; 

    private Callback somethingElseCallback = new Callback() { 
     @Override 
     public void onComplete(String data) { 
      updateUi("somethingElse", data); 
     } 
    }; 

    private Callback yetAnotherThingCallback = new Callback() { 
     @Override 
     public void onComplete(String data) { 
      updateUi("yetAnotherThing", data); 
     } 
    }; 

    private Callable<Void> call(
     final String key, final Callback callback) { 
     return new Callable<Void>() { 
      @Override 
      public Void call() { 
       get(key, callback); 
       return null; 
      } 
     }; 
    } 

    private Callable<Void> allDone() { 
     return new Callable<Void>() { 
      @Override 
      public Void call() { 
       System.out.println("allDone"); 
       return null; 
      } 
     }; 
    } 



    interface Callback 
    { 
     void onComplete(String data); 
    } 
    private void get(String string, Callback callback) { 
     System.out.println("Get "+string); 
     try { 
      Thread.sleep(500); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     callback.onComplete("result of "+string); 
    } 
    private void updateUi(String string, String data) { 
     System.out.println("UpdateUI of "+string+" with "+data); 
    } 

} 

(本例使用invokeAll,该块,直到所有任务已执行此可以通过不同的方式在呼叫站点实现非阻塞,主要想法是创建一个任务列表,这些任务都是由同一个方法调用创建的)

+0

我过分简化了一下问题陈述:在我的情况下,异步操作都是对REST API的HTTP调用。每个电话都是对不同URL的请求。话虽如此,你对我的问题的阅读非常接近!你提出的解决方案很有趣 - 我必须尝试一下,看看它是如何工作的。我还必须寻找一种方法来使其真正实现异步和非阻塞。 – curioustechizen 2014-10-06 12:06:11

+1

@curioustechizen关于*“真正的异步和非阻塞”*:我认为调用/任务必须被阻塞,这意味着一个应该在另一个之后执行(否则你在问题中勾画的“链接”不会使感)。因此,如果这个*“非阻塞”*仅指全部调用序列:通过将每个任务传递给'ExecutorService#submit'方法,您可以单独将任务提交给'ExecutorService',而不是使用'invokeAll' ,在'for'循环中。然后提交任务的线程将继续,任务将在后台处理 – Marco13 2014-10-06 12:25:29

1

Spontainious心想:你可以定义链式调用的方法参数,使您的方法,可重复使用。这里是我的示例代码:

public class Scribble { 

    final Callback step1 = new Callback() { 
     void onComplete(String string) { 
      doSomethingElse(step2); 
     }; 
    }; 

    final Callback step2 = new Callback() { 
     void onComplete(String string) { 
      doYetAnotherThing(step3); 
     }; 
    }; 

    final Callback step3 = new Callback() { 
     void onComplete(String string) { 
      allDone(); 
     } 
    }; 

    private void start() { 
     doSomething(step1); 
    } 

    private void doSomething(final Callback externalCallback) { 
     get("/something", new Callback() { 
      void onComplete(String data) { 
       updateUi(something, data); 
       externalCallback.onComplete(data); 
      } 
     }); 
    } 

    private void doSomethingElse(final Callback externalCallback) { 
     get("/something/else", new Callback() { 
      void onComplete(String data) { 
       updateUi(somethingElse, data); 
       externalCallback.onComplete(data); 
      } 
     }); 
    } 

    private void doYetAnotherThing(final Callback externalCallback) { 
     get("/yet/another/thing", new Callback() { 
      void onComplete(String data) { 
       updateUi(yetAnotherThing, data); 
       externalCallback.onComplete(data); 
      } 
     }); 
    } 

    // - the code below is only to make everything compilable - 

    public class Callback { 

     void onComplete(String string) { 
     } 

    } 

    private Object something; 
    protected Object somethingElse; 
    protected Object yetAnotherThing; 

    protected void allDone() { 
     System.out.println("Scribble.allDone()"); 
    } 

    protected void updateUi(Object yetAnotherThing2, String data) { 
     System.out.println("Scribble.updateUi()"+data); 
    } 

    private void get(String string, Callback callback) { 
     System.out.println("get "+string); 
     callback.onComplete(string); 
    } 

    public static void main(String[] args) { 
     new Scribble().start(); 
    } 

} 
1

我完全支持批准的答案,但是我我也在为这些类型的问题而创建的东西中折腾,这些问题当时派上用场您开始在异步操作链中添加条件逻辑。我最近发酵成一个简单的图书馆(jasync-driver)。

下面是你如何连接你的例子。正如你所看到的,每个任务都不知道下面的任务。与批准的答案相反,任务的链接是通过简单的同步(...查看)方法体而不是列表完成的。

public void doChainedLogic() { 

    final AsyncTask<Void, Void> doSomething = new AsyncTask<Void, Void>() { 
     @Override 
     public void run(Void arg, final ResultHandler<Void> resultHandler) { 
      get("/something", new Callback() { 
       public void onComplete(String data) { 
        updateUi(something, data); 
        resultHandler.reportComplete(); 
       } 
      }); 
     } 
    }; 

    final AsyncTask<Void, Void> doSomethingElse = new AsyncTask<Void, Void>() { 
     @Override 
     public void run(Void arg, final ResultHandler<Void> resultHandler) { 
      get("/something/else", new Callback() { 
       public void onComplete(String data) { 
        updateUi(somethingElse, data); 
        resultHandler.reportComplete(); 
       } 
      }); 
     } 
    }; 

    final AsyncTask<Void, Void> doYetAnotherThing = new AsyncTask<Void, Void>() { 
     @Override 
     public void run(Void arg, final ResultHandler<Void> resultHandler) { 
      get("/yet/another/thing", new Callback() { 
       public void onComplete(String data) { 
        updateUi(yetAnotherThing, data); 
        resultHandler.reportComplete(); 
       } 
      }); 
     } 
    }; 

    // This looks synchronous, but behind the scenes JasyncDriver is 
    // re-executing the body and skipping items already executed. 
    final JasyncDriver driver = new JasyncDriver(); 
    driver.execute(new DriverBody() { 
     public void run() { 
      driver.execute(doSomething); 
      driver.execute(doSomethingElse); 
      driver.execute(doYetAnotherThing); 
     } 
    }); 
} 

现在,这里是一个调整的例子包括取决于异步结果一些有条件的逻辑:

final AsyncTask<Void, String> checkSomething = new AsyncTask<Void, String>() { 
    @Override 
    public void run(Void arg, final ResultHandler<String> resultHandler) { 
     get("/check/something", new Callback() { 
      public void onComplete(String data) { 
       resultHandler.reportComplete(data); 
      } 
     }); 
    } 
}; 

final JasyncDriver driver = new JasyncDriver(); 
driver.execute(new DriverBody() { 
    public void run() { 
     driver.execute(doSomething); 
     if ("foobar".equals(driver.execute(checkSomething))) { 
      driver.execute(doSomethingElse); 
     } 
     driver.execute(doYetAnotherThing); 
    } 
}); 

正如你所看到的,异步的条件逻辑是写if语句标准的那样简单。