前言
不想华丽胡哨的写了,直接说问题和处理办法。
今天遇到一个需求,举个相同的例子说明,就是有两个接口A、B,A是获取某一段范围内用户的姓名列表,接口B中是根据用户的姓名查找这个用户的爱好,那么要通过ListView展示A接口+B接口中的数据,如何优雅的写?
肯定要先调用A接口吧,然后循环调用B接口?,B接口的请求回调中关联上指定的姓名,然后在设置适配器。
不同的人肯定有不同的写法,所以直接上我认为比较优雅的方法吧。
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionService
import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.ExecutorService
import java.util.function.Supplier
import java.util.stream.Collector
import java.util.stream.Collectors
import java.util.stream.Stream
fun syncPost(name: String): String {
return "爱好${(0..10).random()}"
}
var map = mutableMapOf<String, String>()
fun getHobby(name: String, map: MutableMap<String, String>): CompletableFuture<Unit> {
return CompletableFuture.supplyAsync {
Thread.sleep((0..1000).random().toLong())
map[name] = syncPost(name)
}
}
fun main() {
var listName = Stream.generate { "李${(0..10).random()}" }.limit(10).collect(Collectors.toSet())
var exec = mutableListOf<CompletableFuture<Unit>>()
println(map)
listName.forEach {
exec.add(getHobby(it, map))
}
var allOf = CompletableFuture.allOf(*exec.toTypedArray()).thenAccept {
println("执行完成")
println(map)
}
allOf.get()
}
原理还是利用了CompletableFuture,首先 CompletableFuture是Future的扩展,意味着更强,并且解决了Future不能完成的事情,如本例就用到了Future所没有的功能,也就是将多个异步任务一起执行。
还有个更方便的方法,CompletableFuture提供了一个回调功能,这个回调是在所有任务都完成后被调用,也就是thenAccept方法,如果通过循环调用请求,如何判断所有的请求都执行完毕?是不是需要一个变量累加或是别的方法,但是感觉还有没有thenAccept方便吧。
同样我们可以指定超时时间,时间到达后如果还没有全部完成,则抛出TimeoutException。
try {
allOf.get(500, TimeUnit.MILLISECONDS)
} catch (ep: TimeoutException) {
ep.printStackTrace()
}
另外如果在任务中异常没有捕获,那么并不会影响其他的任务,但是不会执行thenAccept,需要注意的是,如果在任务中抛出异常,并不会立马输出,而是在全部任务结束后才被真正抛出。
fun getHobby(name: String, map: MutableMap<String, String>): CompletableFuture<Unit> {
return CompletableFuture.supplyAsync {
Thread.sleep((0..1000).random().toLong())
map[name] = syncPost(name)
if (name == "李2")
throw NullPointerException("null")
println("${Thread.currentThread().name}执行完毕")
}
}
如果某个任务失败,还需要把他加入到一个重试队列中。