亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Retrofit中如何創建掛起函數的調用適配器?

Retrofit中如何創建掛起函數的調用適配器?

HUWWW 2023-01-05 17:07:15
我需要創建一個可以處理此類網絡調用的改造調用適配器:@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    }
查看完整描述

3 回答

?
陪伴而非守候

TA貢獻1757條經驗 獲得超8個贊

這是一個適配器的工作示例,它自動將響應包裝到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()

    ...

}

...



查看完整回答
反對 回復 2023-01-05
?
米脂

TA貢獻1836條經驗 獲得超3個贊

當您使用Retrofit 2.6.0協程時,您不再需要包裝器。它應該如下所示:


@GET("user")

suspend fun getUser(): User

你不再需要MyResponseWrapper了,當你調用它時,它應該看起來像


runBlocking {

   val user: User = service.getUser()

}

要進行改造Response,您可以執行以下操作:


@GET("user")

suspend fun getUser(): Response<User>

您也不需要MyWrapperAdapterFactory或MyWrapperAdapter。


希望這回答了你的問題!


編輯 CommonsWare@ 在上面的評論中也提到了這一點


編輯 處理錯誤可能如下:


sealed class ApiResponse<T> {

    companion object {

        fun <T> create(response: Response<T>): ApiResponse<T> {

            return if(response.isSuccessful) {

                val body = response.body()

                // Empty body

                if (body == null || response.code() == 204) {

                    ApiSuccessEmptyResponse()

                } else {

                    ApiSuccessResponse(body)

                }

            } else {

                val msg = response.errorBody()?.string()

                val errorMessage = if(msg.isNullOrEmpty()) {

                    response.message()

                } else {

                    msg

                }

                ApiErrorResponse(errorMessage ?: "Unknown error")

            }

        }

    }

}


class ApiSuccessResponse<T>(val data: T): ApiResponse<T>()

class ApiSuccessEmptyResponse<T>: ApiResponse<T>()

class ApiErrorResponse<T>(val errorMessage: String): ApiResponse<T>()

您只需要使用響應調用創建,ApiResponse.create(response)它應該返回正確的類型。還可以在此處添加更高級的場景,如果它不僅僅是一個純字符串,則通過解析錯誤。


查看完整回答
反對 回復 2023-01-05
?
白豬掌柜的

TA貢獻1893條經驗 獲得超10個贊

當您使用Retrofit 2.6.0協程時,您不再需要包裝器。它應該如下所示:


@GET("user")

suspend fun getUser(): User

你不再需要MyResponseWrapper了,當你調用它時,它應該看起來像


runBlocking {

   val user: User = service.getUser()

}

要進行改造Response,您可以執行以下操作:


@GET("user")

suspend fun getUser(): Response<User>

您也不需要MyWrapperAdapterFactory或MyWrapperAdapter。


希望這回答了你的問題!


編輯 CommonsWare@ 在上面的評論中也提到了這一點


編輯 處理錯誤可能如下:


sealed class ApiResponse<T> {

    companion object {

        fun <T> create(response: Response<T>): ApiResponse<T> {

            return if(response.isSuccessful) {

                val body = response.body()

                // Empty body

                if (body == null || response.code() == 204) {

                    ApiSuccessEmptyResponse()

                } else {

                    ApiSuccessResponse(body)

                }

            } else {

                val msg = response.errorBody()?.string()

                val errorMessage = if(msg.isNullOrEmpty()) {

                    response.message()

                } else {

                    msg

                }

                ApiErrorResponse(errorMessage ?: "Unknown error")

            }

        }

    }

}


class ApiSuccessResponse<T>(val data: T): ApiResponse<T>()

class ApiSuccessEmptyResponse<T>: ApiResponse<T>()

class ApiErrorResponse<T>(val errorMessage: String): ApiResponse<T>()

您只需要使用響應調用創建,ApiResponse.create(response)它應該返回正確的類型。還可以在此處添加更高級的場景,如果它不僅僅是一個純字符串,則通過解析錯誤。


查看完整回答
反對 回復 2023-01-05
  • 3 回答
  • 0 關注
  • 139 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號