gpt4 book ai didi

java - 如何为 Retrofit 中的挂起功能创建调用适配器?

转载 作者:IT老高 更新时间:2023-10-28 13:36:17 25 4
gpt4 key购买 nike

我需要创建一个可以处理此类网络调用的 retrofit 调用适配器:

@GET("user")
suspend fun getUser(): MyResponseWrapper<User>

我希望它在不使用 Deferred 的情况下与 Kotlin 协程一起工作.我已经使用 Deferred 成功实现了, 可以处理如下方法:

@GET("user")
fun getUser(): Deferred<MyResponseWrapper<User>>

但我希望能够使函数成为暂停函数并删除 Deferred包装。

使用挂起函数,Retrofit 就像有一个 Call 一样工作。返回类型的包装,所以 suspend fun getUser(): User被视为 fun getUser(): Call<User>

我的实现

我已尝试创建一个尝试处理此问题的调用适配器。到目前为止,这是我的实现:

工厂

class MyWrapperAdapterFactory : CallAdapter.Factory() {

override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {

val rawType = getRawType(returnType)

if (rawType == Call::class.java) {

returnType as? ParameterizedType
?: throw IllegalStateException("$returnType must be parameterized")

val containerType = getParameterUpperBound(0, returnType)

if (getRawType(containerType) != MyWrapper::class.java) {
return null
}

containerType as? ParameterizedType
?: throw IllegalStateException("MyWrapper must be parameterized")

val successBodyType = getParameterUpperBound(0, containerType)
val errorBodyType = getParameterUpperBound(1, containerType)

val errorBodyConverter = retrofit.nextResponseBodyConverter<Any>(
null,
errorBodyType,
annotations
)

return MyWrapperAdapter<Any, Any>(successBodyType, errorBodyConverter)
}
return null
}

适配器

class MyWrapperAdapter<T : Any>(
private val successBodyType: Type
) : CallAdapter<T, MyWrapper<T>> {

override fun adapt(call: Call<T>): MyWrapper<T> {
return try {
call.execute().toMyWrapper<T>()
} catch (e: IOException) {
e.toNetworkErrorWrapper()
}
}

override fun responseType(): Type = successBodyType
}
runBlocking {
val user: MyWrapper<User> = service.getUser()
}

使用此实现,一切都按预期工作,但就在网络调用的结果被传递到 user 之前变量,我得到以下错误:

java.lang.ClassCastException: com.myproject.MyWrapper cannot be cast to retrofit2.Call

at retrofit2.HttpServiceMethod$SuspendForBody.adapt(HttpServiceMethod.java:185)
at retrofit2.HttpServiceMethod.invoke(HttpServiceMethod.java:132)
at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
at com.sun.proxy.$Proxy6.getText(Unknown Source)
...

来自 Retrofit 的源代码,这里是 HttpServiceMethod.java:185 的一段代码:

    @Override protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call); // ERROR OCCURS HERE

//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
}

我不确定如何处理此错误。有办法解决吗?

最佳答案

这是一个适配器的工作示例,它自动包装对 Result 包装器的响应。 GitHub 示例也是 available .

// build.gradle

...
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.google.code.gson:gson:2.8.5'
}
// test.kt

...
sealed class Result<out T> {
data class Success<T>(val data: T?) : Result<T>()
data class Failure(val statusCode: Int?) : Result<Nothing>()
object NetworkError : Result<Nothing>()
}

data class Bar(
@SerializedName("foo")
val foo: String
)

interface Service {
@GET("bar")
suspend fun getBar(): Result<Bar>

@GET("bars")
suspend fun getBars(): Result<List<Bar>>
}

abstract class CallDelegate<TIn, TOut>(
protected val proxy: Call<TIn>
) : Call<TOut> {
override fun execute(): Response<TOut> = throw NotImplementedError()
override final fun enqueue(callback: Callback<TOut>) = enqueueImpl(callback)
override final fun clone(): Call<TOut> = cloneImpl()

override fun cancel() = proxy.cancel()
override fun request(): Request = proxy.request()
override fun isExecuted() = proxy.isExecuted
override fun isCanceled() = proxy.isCanceled

abstract fun enqueueImpl(callback: Callback<TOut>)
abstract fun cloneImpl(): Call<TOut>
}

class ResultCall<T>(proxy: Call<T>) : CallDelegate<T, Result<T>>(proxy) {
override fun enqueueImpl(callback: Callback<Result<T>>) = proxy.enqueue(object: Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
val code = response.code()
val result = if (code in 200 until 300) {
val body = response.body()
Result.Success(body)
} else {
Result.Failure(code)
}

callback.onResponse(this@ResultCall, Response.success(result))
}

override fun onFailure(call: Call<T>, t: Throwable) {
val result = if (t is IOException) {
Result.NetworkError
} else {
Result.Failure(null)
}

callback.onResponse(this@ResultCall, Response.success(result))
}
})

override fun cloneImpl() = ResultCall(proxy.clone())
}

class ResultAdapter(
private val type: Type
): CallAdapter<Type, Call<Result<Type>>> {
override fun responseType() = type
override fun adapt(call: Call<Type>): Call<Result<Type>> = ResultCall(call)
}

class MyCallAdapterFactory : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
) = when (getRawType(returnType)) {
Call::class.java -> {
val callType = getParameterUpperBound(0, returnType as ParameterizedType)
when (getRawType(callType)) {
Result::class.java -> {
val resultType = getParameterUpperBound(0, callType as ParameterizedType)
ResultAdapter(resultType)
}
else -> null
}
}
else -> null
}
}

/**
* A Mock interceptor that returns a test data
*/
class MockInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val response = when (chain.request().url().encodedPath()) {
"/bar" -> """{"foo":"baz"}"""
"/bars" -> """[{"foo":"baz1"},{"foo":"baz2"}]"""
else -> throw Error("unknown request")
}

val mediaType = MediaType.parse("application/json")
val responseBody = ResponseBody.create(mediaType, response)

return okhttp3.Response.Builder()
.protocol(Protocol.HTTP_1_0)
.request(chain.request())
.code(200)
.message("")
.body(responseBody)
.build()
}
}

suspend fun test() {
val mockInterceptor = MockInterceptor()
val mockClient = OkHttpClient.Builder()
.addInterceptor(mockInterceptor)
.build()

val retrofit = Retrofit.Builder()
.baseUrl("https://mock.com/")
.client(mockClient)
.addCallAdapterFactory(MyCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()

val service = retrofit.create(Service::class.java)
val bar = service.getBar()
val bars = service.getBars()
...
}
...

关于java - 如何为 Retrofit 中的挂起功能创建调用适配器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56483235/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com