Retrofit中如何创建挂起函数的调用适配器?

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

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

我希望它在不使用Deferred. 我已经成功实现了 using 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

    }


HUWWW
浏览 89回答 3
3回答

陪伴而非守候

这是一个适配器的工作示例,它自动将响应包装到Result包装器。还提供了 GitHub 示例。// build.gradle...dependencies {&nbsp; &nbsp; implementation 'com.squareup.retrofit2:retrofit:2.6.1'&nbsp; &nbsp; implementation 'com.squareup.retrofit2:converter-gson:2.6.1'&nbsp; &nbsp; implementation 'com.google.code.gson:gson:2.8.5'}// test.kt...sealed class Result<out T> {&nbsp; &nbsp; data class Success<T>(val data: T?) : Result<T>()&nbsp; &nbsp; data class Failure(val statusCode: Int?) : Result<Nothing>()&nbsp; &nbsp; object NetworkError : Result<Nothing>()}data class Bar(&nbsp; &nbsp; @SerializedName("foo")&nbsp; &nbsp; val foo: String)interface Service {&nbsp; &nbsp; @GET("bar")&nbsp; &nbsp; suspend fun getBar(): Result<Bar>&nbsp; &nbsp; @GET("bars")&nbsp; &nbsp; suspend fun getBars(): Result<List<Bar>>}abstract class CallDelegate<TIn, TOut>(&nbsp; &nbsp; protected val proxy: Call<TIn>) : Call<TOut> {&nbsp; &nbsp; override fun execute(): Response<TOut> = throw NotImplementedError()&nbsp; &nbsp; override final fun enqueue(callback: Callback<TOut>) = enqueueImpl(callback)&nbsp; &nbsp; override final fun clone(): Call<TOut> = cloneImpl()&nbsp; &nbsp; override fun cancel() = proxy.cancel()&nbsp; &nbsp; override fun request(): Request = proxy.request()&nbsp; &nbsp; override fun isExecuted() = proxy.isExecuted&nbsp; &nbsp; override fun isCanceled() = proxy.isCanceled&nbsp; &nbsp; abstract fun enqueueImpl(callback: Callback<TOut>)&nbsp; &nbsp; abstract fun cloneImpl(): Call<TOut>}class ResultCall<T>(proxy: Call<T>) : CallDelegate<T, Result<T>>(proxy) {&nbsp; &nbsp; override fun enqueueImpl(callback: Callback<Result<T>>) = proxy.enqueue(object: Callback<T> {&nbsp; &nbsp; &nbsp; &nbsp; override fun onResponse(call: Call<T>, response: Response<T>) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val code = response.code()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val result = if (code in 200 until 300) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val body = response.body()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result.Success(body)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result.Failure(code)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callback.onResponse(this@ResultCall, Response.success(result))&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; override fun onFailure(call: Call<T>, t: Throwable) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val result = if (t is IOException) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result.NetworkError&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result.Failure(null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callback.onResponse(this@ResultCall, Response.success(result))&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; })&nbsp; &nbsp; override fun cloneImpl() = ResultCall(proxy.clone())}class ResultAdapter(&nbsp; &nbsp; private val type: Type): CallAdapter<Type, Call<Result<Type>>> {&nbsp; &nbsp; override fun responseType() = type&nbsp; &nbsp; override fun adapt(call: Call<Type>): Call<Result<Type>> = ResultCall(call)}class MyCallAdapterFactory : CallAdapter.Factory() {&nbsp; &nbsp; override fun get(&nbsp; &nbsp; &nbsp; &nbsp; returnType: Type,&nbsp; &nbsp; &nbsp; &nbsp; annotations: Array<Annotation>,&nbsp; &nbsp; &nbsp; &nbsp; retrofit: Retrofit&nbsp; &nbsp; ) = when (getRawType(returnType)) {&nbsp; &nbsp; &nbsp; &nbsp; Call::class.java -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val callType = getParameterUpperBound(0, returnType as ParameterizedType)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; when (getRawType(callType)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result::class.java -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val resultType = getParameterUpperBound(0, callType as ParameterizedType)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ResultAdapter(resultType)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else -> null&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; else -> null&nbsp; &nbsp; }}/**&nbsp;* A Mock interceptor that returns a test data&nbsp;*/class MockInterceptor : Interceptor {&nbsp; &nbsp; override fun intercept(chain: Interceptor.Chain): okhttp3.Response {&nbsp; &nbsp; &nbsp; &nbsp; val response = when (chain.request().url().encodedPath()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "/bar" -> """{"foo":"baz"}"""&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "/bars" -> """[{"foo":"baz1"},{"foo":"baz2"}]"""&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else -> throw Error("unknown request")&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; val mediaType = MediaType.parse("application/json")&nbsp; &nbsp; &nbsp; &nbsp; val responseBody = ResponseBody.create(mediaType, response)&nbsp; &nbsp; &nbsp; &nbsp; return okhttp3.Response.Builder()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .protocol(Protocol.HTTP_1_0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .request(chain.request())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .code(200)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .message("")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .body(responseBody)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build()&nbsp; &nbsp; }}suspend fun test() {&nbsp; &nbsp; val mockInterceptor = MockInterceptor()&nbsp; &nbsp; val mockClient = OkHttpClient.Builder()&nbsp; &nbsp; &nbsp; &nbsp; .addInterceptor(mockInterceptor)&nbsp; &nbsp; &nbsp; &nbsp; .build()&nbsp; &nbsp; val retrofit = Retrofit.Builder()&nbsp; &nbsp; &nbsp; &nbsp; .baseUrl("https://mock.com/")&nbsp; &nbsp; &nbsp; &nbsp; .client(mockClient)&nbsp; &nbsp; &nbsp; &nbsp; .addCallAdapterFactory(MyCallAdapterFactory())&nbsp; &nbsp; &nbsp; &nbsp; .addConverterFactory(GsonConverterFactory.create())&nbsp; &nbsp; &nbsp; &nbsp; .build()&nbsp; &nbsp; val service = retrofit.create(Service::class.java)&nbsp; &nbsp; val bar = service.getBar()&nbsp; &nbsp; val bars = service.getBars()&nbsp; &nbsp; ...}...

米脂

当您使用Retrofit 2.6.0协程时,您不再需要包装器。它应该如下所示:@GET("user")suspend fun getUser(): User你不再需要MyResponseWrapper了,当你调用它时,它应该看起来像runBlocking {&nbsp; &nbsp;val user: User = service.getUser()}要进行改造Response,您可以执行以下操作:@GET("user")suspend fun getUser(): Response<User>您也不需要MyWrapperAdapterFactory或MyWrapperAdapter。希望这回答了你的问题!编辑 CommonsWare@ 在上面的评论中也提到了这一点编辑 处理错误可能如下:sealed class ApiResponse<T> {&nbsp; &nbsp; companion object {&nbsp; &nbsp; &nbsp; &nbsp; fun <T> create(response: Response<T>): ApiResponse<T> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return if(response.isSuccessful) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val body = response.body()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Empty body&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (body == null || response.code() == 204) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiSuccessEmptyResponse()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiSuccessResponse(body)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val msg = response.errorBody()?.string()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val errorMessage = if(msg.isNullOrEmpty()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.message()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiErrorResponse(errorMessage ?: "Unknown error")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}class ApiSuccessResponse<T>(val data: T): ApiResponse<T>()class ApiSuccessEmptyResponse<T>: ApiResponse<T>()class ApiErrorResponse<T>(val errorMessage: String): ApiResponse<T>()您只需要使用响应调用创建,ApiResponse.create(response)它应该返回正确的类型。还可以在此处添加更高级的场景,如果它不仅仅是一个纯字符串,则通过解析错误。

白猪掌柜的

当您使用Retrofit 2.6.0协程时,您不再需要包装器。它应该如下所示:@GET("user")suspend fun getUser(): User你不再需要MyResponseWrapper了,当你调用它时,它应该看起来像runBlocking {&nbsp; &nbsp;val user: User = service.getUser()}要进行改造Response,您可以执行以下操作:@GET("user")suspend fun getUser(): Response<User>您也不需要MyWrapperAdapterFactory或MyWrapperAdapter。希望这回答了你的问题!编辑 CommonsWare@ 在上面的评论中也提到了这一点编辑 处理错误可能如下:sealed class ApiResponse<T> {&nbsp; &nbsp; companion object {&nbsp; &nbsp; &nbsp; &nbsp; fun <T> create(response: Response<T>): ApiResponse<T> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return if(response.isSuccessful) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val body = response.body()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Empty body&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (body == null || response.code() == 204) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiSuccessEmptyResponse()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiSuccessResponse(body)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val msg = response.errorBody()?.string()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val errorMessage = if(msg.isNullOrEmpty()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.message()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ApiErrorResponse(errorMessage ?: "Unknown error")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}class ApiSuccessResponse<T>(val data: T): ApiResponse<T>()class ApiSuccessEmptyResponse<T>: ApiResponse<T>()class ApiErrorResponse<T>(val errorMessage: String): ApiResponse<T>()您只需要使用响应调用创建,ApiResponse.create(response)它应该返回正确的类型。还可以在此处添加更高级的场景,如果它不仅仅是一个纯字符串,则通过解析错误。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java