如何为Retrofit中的挂起功能创建呼叫适配器?
我需要创建一个可处理此类网络呼叫的改造呼叫适配器:
@GET("user")suspend fun getUser(): MyResponseWrapper<User>
我希望它不使用Kotlin Coroutines Deferred。我已经有了使用的成功实现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示例也可用。
// 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()
    ...
}
...
以上是 如何为Retrofit中的挂起功能创建呼叫适配器? 的全部内容, 来源链接: utcz.com/qa/403263.html





