如何为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

回到顶部