因为后盾返回对立数据结构,比方 code, data, message; 应用过 Retrofit 的同学肯定定义过相似 BaseResponse 这品种,然而 BaseResponse 的解决逻辑都大同小异, 每次都写着实让人很烦,有没有什么好的形式解决这一痛点呢?本文讲介绍一种优雅的形式 来解决这一问题。

背景

当咱们关上 Retrofit 的官网文档时,官网的例子是这样的:

public interface GitHubService {  @GET("users/{user}/repos")  Call<List<Repo>> listRepos(@Path("user") String user);}

而到了咱们本人的我的项目,大多状况的确这样的:

public interface UserService {  @GET("/users")  Call<BaseResponse<List<User>>> getAllUsers();}

而不同的公司,有不同的数据结构,不过都是大同小异,比方 code, data, message 或者 status, data, message, 每次都要写什么 code == 0 之类的代码,无聊不说,次要一点 技术含量都没有。。。

如果咱们要是能把 BaseResponse 去掉,岂不美哉?就像上面的定义一样:

public interface UserService {  @GET("/users")  Call<List<User>> getAllUsers();}

如果是 Kotlin 就更爽了,间接 suspend 办法走起。

interface UserService {  @GET("/users")  suspend fun getAllUsers() : List<User>}

1.Convert.Factory 中被疏忽的参数

public interface Converter<F, T> {  abstract class Factory {    // 参数 annotations 是 Method 上的注解    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(        Type type, Annotation[] annotations, Retrofit retrofit) {      return null;    }  }}public final class GsonConverterFactory extends Converter.Factory {  // Retrofit 官网的 Converter.Factory 并没有应用 annotations 这个参数  @Override  public Converter<ResponseBody, ?> responseBodyConverter(      Type type, Annotation[] annotations, Retrofit retrofit) {    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));    return new GsonResponseBodyConverter<>(gson, adapter);  }}

从下面的代码不难看出,在实现 Convert.Factory 的时候, Retrofit 官网的实现并没有应用 annotations 这个参数,而这个参数恰好是移除 BaseResponse的要害。

2.实现形式

其实实现计划很简略,就是给 Method 再加一个自定义注解,而后对应实现一个 Converter.Factory, 再这个 Converter.Factory 类中判断 Method 是否存在咱们自定义的 注解,如果有就将数据进行转换,最初交给 Gson 进行解析。

我的项目地址:Convex

https://github.com/ParadiseHe...

实现细节

数据转换接口:

interface ConvexTransformer {    // 将原始的 InputStream 转换成具体业务数据的 InputStream    // 相当于获取 code, data, message 的数据流转换成只有 data 的数据流    @Throws(IOException::class)    fun transform(original: InputStream): InputStream}

自定义注解:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)@Retention(AnnotationRetention.RUNTIME)annotation class Transformer(val value: KClass<out ConvexTransformer>)// 有了这个自定义注解,咱们就能够给 Method 增加这个注解了interface UserService {    @GET("xxx")    @Transformer(XXXTransformer::class)    fun xxxMethod() : Any}

Convert.Factory 实现类:

class ConvexConverterFactory : Converter.Factory() {    override fun responseBodyConverter(        type: Type,        annotations: Array<Annotation>,        retrofit: Retrofit    ): Converter<ResponseBody, *>? {        // 获取 Method 上的 Transformer 注解        // 从而确定应用那个具体的 ConvexTransformer 类        return annotations            .filterIsInstance<Transformer>()            .firstOrNull()?.value            ?.let { transformerClazz ->                // 获取能够解决以后返回值的 Converter                // ConvexConverterFactory 只做数据流转,具体数据序列化                // 交给相似 GsonConverterFactory                retrofit.nextResponseBodyConverter<Any>(                    this, type, annotations                )?.let { converter ->                    // 如果有能解决返回值的 Converter 创立代理的 ConvexConveter                    ConvexConverter<Any>(transformerClazz, converter)                }            }    }}class ConvexConverter<T> constructor(    private val transformerClazz: KClass<out ConvexTransformer>,    private val candidateConverter: Converter<ResponseBody, *>) : Converter<ResponseBody, T?> {    @Suppress("UNCHECKED_CAST")    @Throws(IOException::class)    override fun convert(value: ResponseBody): T? {        // 从 Convex 中获取 ConvexTransformer        // Convex 是一个 ServiceRegistry, 用于存储 ConvexTransformer        // 如果没有 ConvexTransformer 便会通过反射创立对应的 ConvexTransformer        // 并保留进 Convex 中期待下次应用        return Convex.getConvexTransformer(transformerClazz.java)            .let { transformer ->                // 转换数据流,这里就是将 code, data, message 数据流转换成 data 数据流                transformer.transform(value.byteStream()).let { responseStream ->                    ResponseBody.create(value.contentType(), responseStream.readBytes())                }            }?.let { responseBody ->                // 应用具体 Converter 将 data 数据流转换成具体的 data                candidateConverter.convert(responseBody) as T?            }    }}

3.Convex 应用

增加依赖

dependencies {    implementation "org.paradisehell.convex:convex:1.0.0"}

实现 ConvexTransformer

private class TestConvexTransformer : ConvexTransformer {    @Throws(IOException::class)    override fun transform(original: InputStream): InputStream {        TODO("Return the business data InputStream.")    }}

将 ConvexConverterFactory 退出 Retrofit 中

Retrofit.Builder()    .baseUrl("https://test.com/")    // ConvexConverterFactory 肯定放在最后面    .addConverterFactory(ConvexConverterFactory())    .addConverterFactory(GsonConverterFactory.create())    .build()

定义 Service 接口

interface XXXService {    @GET("/users")    // 应用 Transformer 注解,增加具体的 ConvexTransformer 实现类    @Transformer(TestConvexTransformer::class)    suspend fun getAllUsers() : List<User>}

4.例子

首先非常感谢 WanAndroid 提供的收费 API。

https://www.wanandroid.com/bl...

// 1. 定义 BaseResponse// 用于解决后盾返回的数据进行反序列化,拿到最终的 data 数据data class BaseResponse<T>(    @SerializedName("errorCode")    val errorCode: Int = 0,    @SerializedName("errorMsg")    val errorMsg: String? = null,    @SerializedName("data")    val data: T? = null)// 2. 实现 ConvexTransformer// 用户将后盾返回的数据流转为具体的 data 数据class WanAndroidConvexTransformer : ConvexTransformer {    private val gson = Gson()    @Throws(IOException::class)    override fun transform(original: InputStream): InputStream {        // 先反序列化为 BaseResponse        val response = gson.fromJson<BaseResponse<JsonElement>>(            original.reader(),            object : TypeToken<BaseResponse<JsonElement>>() {            }.type        )        // 判断 Response 是否胜利        // 胜利则将 data 数据转换成 InputStream, 最初由具体 Converter 解决        if (response.errorCode == 0 && response.data != null) {            return response.data.toString().byteInputStream()        }        throw IOException(            "errorCode : " + response.errorCode + " ; errorMsg : " + response.errorMsg        )    }}// 3. 定义模型数据data class Article(    @SerializedName("id")    val id: Int = 0,    @SerializedName("link")    val link: String? = null,    @SerializedName("author")    val author: String? = null,    @SerializedName("superChapterName")    val superChapterName: String? = null)// 4. 定义 Serviceinterface WanAndroidService {    @GET("/article/top/json")    // 为改办法指定 ConvexTransformer, 这样就能够将 BaseResponse 转换成 data 了    @Transformer(WanAndroidConvexTransformer::class)    suspend fun getTopArticles(): List<Article>}// 5. 定义 Retrofitprivate val retrofit by lazy {    Retrofit.Builder()        .baseUrl("https://wanandroid.com/")        // 肯定将 ConvexConverterFactory 放在所有 Converter.Factory 的后面        .addConverterFactory(ConvexConverterFactory())        .addConverterFactory(GsonConverterFactory.create())        .build()}private val wanAndroidService by lazy {    retrofit.create(WanAndroidService::class.java)}// 6. 执行 Service 办法lifecycleScope.launch(Main) {    val result = withContext(IO) {        // 须要进行 try catch 操作, 因为申请失败会抛出异样        runCatching {            wanAndroidService.getTopArticles()        }.onSuccess {            return@withContext it        }.onFailure {            it.printStackTrace()            return@withContext it        }    }    // 打印数据    // 胜利打印数据列表,失败打印 Exeception    printlin(result)}

小结

完满,当前定义 Method 再也不必写 BaseResponse 了,BaseResponse 终于能够对立解决了。

而且这种计划还反对多种不同的数据类型,因为不同的 Method 能够指定不同的 ConvexTransformer, 而到具体的业务解决基本不必关系 BaseResponse 是如何解决的, 因为具体的业务代码拿到的都是具体的 Data 数据。

不得不拜服 Retrofit 的设计,Converter.Factory 留出了 annotations 这个参数,可扩大 性几乎强到爆,致敬 Square, Salute~~

Android高级开发零碎进阶笔记、最新面试温习笔记PDF,我的GitHub

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!