在不阻塞主线程的情况下检索HTTP响应

时间:2017-05-23 19:58:44

标签: java android multithreading concurrency nonblocking

我目前正在将用Objective-C编写的网络库移植到Java,我需要向服务器发出异步HTTP请求的功能,而不依赖于任何第三方库(因此,没有Apache依赖项)。我正在使用HTTPUrlConnection来执行此操作;但是,正如您所知,这是一个同步过程。

我正在尝试使用ExecutorServiceFuture<>结构在此过程中实现并发。我需要使用Callable而不是Runnable,因为我需要处理服务器发回给我的响应消息。以下是我正在做的一个小概述:

ExecutorService executorService = Executors.newSingleThreadExecutor();
  Future<String> future = executorService.submit(new Callable<String>() {
     @Override
     public String call() throws Exception {
        return postRequest(reqMsg);
     }
  });

其中reqMsg是请求消息(以XML格式),postRequest方法将响应消息作为String(也以XML格式)返回。现在,为了检索此Callable任务的返回值,我需要使用future.get()。但是,此方法是阻塞调用,这意味着它会阻止主线程,直到响应可用。这不是我想要的。

我正在将一个iOS应用程序移植到Android,我将使用J2ObjC在这些平台之间建立一个跨平台的共享库。在iOS版本(意味着库的Obj-C版本)中,有完成处理程序处理来自HTTP请求的结果,并且它是异步进行的。遗憾的是,Java使用CompletableFuture在Java 8中引入了这种处理它的回调方式。但是,Android仅支持从API级别24开始的CompletableFuture。我希望能够支持返回Jelly Bean(API 16)的API级别。据我所知,“对于与Android兼容的JAR文件,它只能引用可用作Android的一部分的类以及在JAR本身专门实现的其他类”。您可能会建议使用AsyncTasks;但是,我想在Java网络端处理HTTP请求的并发性,因为这个库很可能在两个平台之间共享。

我已经尝试过明确地使用Threads;然而,当我研究它时,我发现为了使用Callables,你需要使用ExecutorService和Future(另外,似乎有一些与显式创建Threads相关的性能开销 - 尽管我认为&lt; 1000ms的开销是上可接受的)。再次,重申(即 TL:DR ):

  • 我不想依赖第三方库,除非它是绝对必要的。

  • 我无法使用CompletableFuture,因为我希望我的最低API级别为16。

  • 我不想在Android端用AsyncTasks处理这种并发性,因为最初,这是在网络库中用CompletionHandler(Objective-C)处理的。

有没有办法在不阻止的情况下使用Future.get()? (提醒一下,while循环检查Future.isDone()也会阻塞主线程。)

2 个答案:

答案 0 :(得分:2)

调用Future.get()是阻止操作,您无法更改。

这里你想要的是在工作完成后获得回调。为此,您根本不必使用Future。您始终可以Executor.execute()Runnable使用与Callable完全相同的内容,但不应返回值,而应调用自定义 - 在方法参数中提供 - 使用值进行回调。基本上相当于Java 8中引入的Consumer接口。

ExecutorService executorService = Executors.newSingleThreadExecutor();

public void execute(YourRequestObject reqMsg, Consumer<String> consumer) {
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            String result = postRequest(reqMsg);
            consumer.accept(result);
        }
    });
}

每次都不要创建执行者。只需使用一个执行程序即可完成所有操作。也许是缓存的而不是singleThread的。

请记住,您的回调将在执行程序线程上调用。如果您想更新UI,您需要在UI处理程序上发布帖子。

答案 1 :(得分:0)

你的问题没有多大意义。一方面,您希望在单独的线程中获取数据,但同时在另一个线程中获取数据,而无需等待获取线程完成。

你需要做的是将fetch和“对获取的数据执行任何操作”包装到Runnable中并在单独的线程中运行它。

确保在事件调度线程中更新UI,以防您需要使用获取的数据进行更新。