在不阻塞主线程的情况下检索 HTTP 响应
Retrieving HTTP response without blocking the main thread
我目前正在将一个用 Objective-C 编写的网络库移植到 Java,我需要向服务器发出异步 HTTP 请求的功能,而不依赖任何第三方库(所以,没有 Apache 依赖项)。我正在使用 HTTPUrlConnection
来执行此操作;但是,如您所知,这是一个同步过程。
我正在尝试使用 ExecutorService
和 Future<>
结构在此进程中实现并发。我需要使用 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 方法 return 是字符串形式的响应消息(也在 XML 中)。现在,为了检索此 Callable 任务的 return 值,我需要使用 future.get()
。但是,此方法是一个阻塞调用,这意味着它会阻塞主线程,直到响应可用。这不是我想要的。
我正在将 iOS 应用程序移植到 Android,我将使用 J2ObjC 在这些平台之间建立一个跨平台共享库。在 iOS 版本(即库的 Obj-C 版本)中,有处理 HTTP 请求结果的完成处理程序,并且它是异步生成的。 Java,不幸的是,在 Java 8 中引入了这种处理它的回调方式,使用 CompletableFuture。但是,Android 仅支持从 API 级别 24 开始的 CompletableFuture。我希望能够支持返回到 Jelly Bean (API 16) 的 API 级别。据我所知,"for a JAR file to be compatible with Android, it can only reference classes available as part of Android and other classes implemented specifically in the JAR itself"。您可能会建议使用 AsyncTasks;但是,我想在 Java 网络端处理 HTTP 请求的并发,因为这个库很可能会在两个平台之间共享。
我试过明确地使用线程;然而,正如我研究的那样,我发现为了使用 Callables,你需要使用 ExecutorService 和 Future(另外,似乎有一些与显式创建线程相关的性能开销 - 尽管我认为 < 1000ms 的开销是可以接受的).再次重申(即 TL:DR):
我不想依赖第三方库,除非绝对必要。
我不能使用 CompletableFuture
,因为我希望我的最低 API 等级是 16。
我不想用 AsyncTasks
在 Android 端处理这种并发,因为最初,这是在网络库中用 [=19= 处理的](Objective-C)。
有没有办法不阻塞地使用Future.get()
? (温馨提醒,while 循环检查 Future.isDone()
也会阻塞主线程)。
你的问题没有多大意义。一方面你想在单独的线程中获取数据,但同时在另一个线程中获取它而不等待获取线程完成。
您需要做的是将 fetch 和 "do whatever with the fetched data" 包装到 Runnable 中,并将其 运行 包装在一个单独的线程中。
确保在事件调度线程中更新 UI,以防您需要用获取的数据更新它。
调用 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);
}
});
}
也不要每次都创建执行器。只需使用一个执行器即可完成所有操作。也许是缓存的而不是单线程的。
请记住,您的回调将在执行程序线程上调用。如果您想更新 UI,您需要在 UI 处理程序上创建一个 post。
我目前正在将一个用 Objective-C 编写的网络库移植到 Java,我需要向服务器发出异步 HTTP 请求的功能,而不依赖任何第三方库(所以,没有 Apache 依赖项)。我正在使用 HTTPUrlConnection
来执行此操作;但是,如您所知,这是一个同步过程。
我正在尝试使用 ExecutorService
和 Future<>
结构在此进程中实现并发。我需要使用 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 方法 return 是字符串形式的响应消息(也在 XML 中)。现在,为了检索此 Callable 任务的 return 值,我需要使用 future.get()
。但是,此方法是一个阻塞调用,这意味着它会阻塞主线程,直到响应可用。这不是我想要的。
我正在将 iOS 应用程序移植到 Android,我将使用 J2ObjC 在这些平台之间建立一个跨平台共享库。在 iOS 版本(即库的 Obj-C 版本)中,有处理 HTTP 请求结果的完成处理程序,并且它是异步生成的。 Java,不幸的是,在 Java 8 中引入了这种处理它的回调方式,使用 CompletableFuture。但是,Android 仅支持从 API 级别 24 开始的 CompletableFuture。我希望能够支持返回到 Jelly Bean (API 16) 的 API 级别。据我所知,"for a JAR file to be compatible with Android, it can only reference classes available as part of Android and other classes implemented specifically in the JAR itself"。您可能会建议使用 AsyncTasks;但是,我想在 Java 网络端处理 HTTP 请求的并发,因为这个库很可能会在两个平台之间共享。
我试过明确地使用线程;然而,正如我研究的那样,我发现为了使用 Callables,你需要使用 ExecutorService 和 Future(另外,似乎有一些与显式创建线程相关的性能开销 - 尽管我认为 < 1000ms 的开销是可以接受的).再次重申(即 TL:DR):
我不想依赖第三方库,除非绝对必要。
我不能使用
CompletableFuture
,因为我希望我的最低 API 等级是 16。我不想用
AsyncTasks
在 Android 端处理这种并发,因为最初,这是在网络库中用 [=19= 处理的](Objective-C)。
有没有办法不阻塞地使用Future.get()
? (温馨提醒,while 循环检查 Future.isDone()
也会阻塞主线程)。
你的问题没有多大意义。一方面你想在单独的线程中获取数据,但同时在另一个线程中获取它而不等待获取线程完成。
您需要做的是将 fetch 和 "do whatever with the fetched data" 包装到 Runnable 中,并将其 运行 包装在一个单独的线程中。
确保在事件调度线程中更新 UI,以防您需要用获取的数据更新它。
调用 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);
}
});
}
也不要每次都创建执行器。只需使用一个执行器即可完成所有操作。也许是缓存的而不是单线程的。
请记住,您的回调将在执行程序线程上调用。如果您想更新 UI,您需要在 UI 处理程序上创建一个 post。