因为后盾返回对立数据结构,比方 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. 定义 Service
interface WanAndroidService {@GET("/article/top/json")
// 为改办法指定 ConvexTransformer, 这样就能够将 BaseResponse 转换成 data 了
@Transformer(WanAndroidConvexTransformer::class)
suspend fun getTopArticles(): List<Article>}
// 5. 定义 Retrofit
private 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 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!