Retrofit 自定义 Convertor

围巾🧣 2024年04月29日 296次浏览

例如一般网络响应都是这样封装,code msg data,很多时候只要使用 data,code 有错误就统一逻辑处理了。这时就需要一个 convertor 转换直接取出 data

编写 retrofit 的service 就方法返回值就不用都是 Call<HttpData<DataT>> 层层套娃了,变成了 Call<DataT>, 使用 Kt 挂起函数直接变成 DataT

直接上代码:

HttpData

/**
 *    desc   : 统一接口数据结构
 */
open class HttpData<T> {

    /** 返回码 */
    private val code: Int = 0

    /** 提示语 */
    private val message: String? = null

    /** 数据 */
    private val data: T? = null

    fun getCode(): Int {
        return code
    }

    fun getMessage(): String? {
        return message
    }

    fun getData(): T? {
        return data
    }

    /**
     * 是否请求成功
     */
    fun isRequestSucceed(): Boolean {
        return code == 200
    }

    /**
     * 是否 Token 失效
     */
    fun isTokenFailure(): Boolean {
        return code == 1001
    }
}

CustomGsonConverterFactory.kt

class CustomGsonConverterFactory private constructor(private var gson: Gson) : Converter.Factory() {

    companion object {
        fun create(): CustomGsonConverterFactory {
            return create(Gson())
        }

        fun create(gson: Gson): CustomGsonConverterFactory {
            return CustomGsonConverterFactory(gson)
        }
    }

    @EverythingIsNonNull
    override fun responseBodyConverter(
        type: Type,
        annotations: Array<out Annotation>,
        retrofit: Retrofit
    ): Converter<ResponseBody, *> {
        val adapter = gson.getAdapter(TypeToken.get(type))
        return CustomResponseBodyConverter(gson, adapter, TypeToken.get(type))
    }


    /**
     * 自定义 GsonResponseBodyConverter, 转化 ResponseBody 中的 `data`为对象,
     * 并对返回错误的情况进行异常处理
     */
    private inner class CustomResponseBodyConverter<T> constructor(
        private val gson: Gson,
        private val adapter: TypeAdapter<T>,
        private val token: TypeToken<*>
    ) :
        Converter<ResponseBody, T?> {
        @Throws(IOException::class)
        override fun convert(value: ResponseBody): T? {
            val base = gson.fromJson(value.charStream(), HttpData::class.java)
            // 错误处理
            if (!base.isRequestSucceed()) {
                throw ApiException(base.getCode(), base.getMessage())
            }
            // 返回数据解析, use 结束后调用 values 的 close
            return value.use { _ ->
                val basic = checkBasicType(base)
                if (basic != null) {
                    return basic as T
                }
                if (base.getData() == null) null else adapter.fromJson(gson.toJson(base.getData()))
            }
        }

        /**
         * 判断返回数据是不是基本数据类型(包含String)。如果是,则转化后返回。
         *
         * @param httpData 返回数据对象
         * @return 转化后的对象,如果不是基本类型,则返回null
         */
        private fun checkBasicType(httpData: HttpData<*>): Any? {
            val typeName = token.rawType.simpleName
            val data = httpData.getData().toString()
            return when (typeName) {
                "String" -> data
                "Boolean" -> Boolean.parseBoolean(data)
                "Integer" -> data.toInt()
                else -> null
            }
        }
    }

    override fun requestBodyConverter(
        type: Type,
        parameterAnnotations: Array<out Annotation>,
        methodAnnotations: Array<out Annotation>,
        retrofit: Retrofit
    ): Converter<*, RequestBody>? {
        val adapter = gson.getAdapter(TypeToken.get(type))
        return CustomRequestBodyConverter(gson, adapter)
    }

    /**
     * 与 GsonConverterFactory 一致
     */
    private inner class CustomRequestBodyConverter<T> constructor(gson: Gson, adapter: TypeAdapter<T>) :
        Converter<T, RequestBody> {
        private val MEDIA_TYPE: MediaType = "application/json; charset=UTF-8".toMediaType()
        private val UTF_8 = StandardCharsets.UTF_8
        private val gson: Gson
        private val adapter: TypeAdapter<T>

        init {
            this.gson = gson
            this.adapter = adapter
        }

        override fun convert(value: T): RequestBody {
            val buffer = Buffer()
            val writer = OutputStreamWriter(buffer.outputStream(), UTF_8)
            val jsonWriter = gson.newJsonWriter(writer)
            adapter.write(jsonWriter, value)
            jsonWriter.close()
            return buffer.readByteString().toRequestBody(MEDIA_TYPE)
        }
    }
}