最近用kotlin开发服务端,用了很多库,像Spring boot的RestTemplate,okhttp还有retrofit,觉得retrofit很好用。但是,在用retrofit来实现协程的时候,遇到了一个问题
如图所示,提示Inappropriate blocking method call,说这种协程的使用方式是不合适的。我在Stack Overflow上搜了一下,发现如下解答
也就是说这个execute方法会阻塞当前线程,因此不能令线程挂起,导致协程失效,而这个execute的原方法中:
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
内部用了一个大的synchronized内部锁,导致线程阻塞。因此这种获取HTTP数据的方式用协程来实现是没有效果的,这里我们选择另一个提到的ktor client来实现。
ktor client是kotlin官方提供的一个用于读取HTTP数据的一种工具库。而父组件ktor是一个用纯kotlin实现的一个服务端,类似Spring Boot,这里我们只需要他获取HTTP数据的功能
导库,一定要导入全,kotlin官方是推荐Gradle,但是我这里用的maven。导入的包比较多也比较烦
<-- 导入CIO库,这是一个支持协程的版本 -->
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-cio</artifactId>
<version>1.3.1</version>
</dependency>
<-- 用于支持json的解析 -->
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-json</artifactId>
<version>1.3.1</version>
</dependency>
<-- 选择Gson作为解析的工具库 -->
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-gson</artifactId>
<version>1.3.1</version>
</dependency>
<-- 协程库-->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.3.4</version>
</dependency>
<-- 采用jdk8编译 -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<-- 解析json涉及到序列化,一定要导入反射包 -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.72</version>
<scope>runtime</scope>
</dependency>
以上的包一定要全部导完,一个不能少。至于有一个kotlinx serialization的库,用来标识序列化的数据类,我试了试,不用也可以。
接下来在IDEA中下载插件
用过GsonFormatter的同学应该会比较熟悉,这个是kotlin的将JSON转换成对应的数据类的工具,接下来转换的时候注意设置:
Enable Inner Class Model要勾上,这样JSON内部还有类的话也可以解析。
这是一个数据类型的模板:
@SerializedName来标识序列化的JSON String,因为属性里面是不允许出现特殊字符,而JSON String里面是可以有的。
同时,需要给每个值赋一个初始值,这样才会有一个无参的构造函数,我之前用fastjson有这个问题,不知道gson是不是这样的
data class Coordinate(
val X: Double = 0.0,
val Y: Double = 0.0,
val Z: Double = 0.0
)
接下来就是用ktor client来解析数据了。
首先定义HTTP Client:
// 指定解析引擎为CIO(Coroutine-based I/O),也是kotlin官方推荐的引擎
// 之所以CIO后面没加括号是因为CIO是一个单例类
val httpClient = HttpClient(CIO){
//这个必须要在引入ktor-client-gson后才可以使用,用于指定解析工具
install(JsonFeature){
serializer = GsonSerializer()
}
}
用定义好的httpClient从http获取数据
// 注意给网络通信指定IO协程池
val absoluteValues = async(Dispatchers.IO) {
// 指定HTTP方法,以及返回的JSON字符串对应的类。后面的run其实是把结果换成了一个新的类的列表
httpClient.get<Coordinate>("http://localhost:8080/coors/absolute").run {
sequenceOf(X, Y, Z).map { DataValue(Variant(it)) }.toList()
}
}
接下来就可以愉快的用协程来实现HTTP的读写了~