概述
网络层功能搭建,使用技术: 协成 + Retrofit + Okhttp + Moshi。
一、支持功能
-
实现优雅调用网络请求:支持多Domain。
-
网络请求公共参数:GET 参数、POST url 参数、POST Formbody 参数。
-
网络请求唯一ID:请求时带入,完成后带回。方便测试、bug统计。
-
统一接口返回回调:统一处理错误码,log打点统计。
-
去信封功能:接口返回如下的json格式, 直接解析成data对应的model。
{
"errorCode": 0,
"errorMsg": "",
"data": {
"title": "this is title"
}
}
二、使用方式
网络请求事例
@JsonClass(generateAdapter = true)
@CropEnvelope //CropEnvelope注解即可实现去信封功能 (前提是Response返回类型)
data class WanAndroidCropBanner(
@Json(name = "title")
val title: String
)
interface WanAndroidService {
@GET("/banner/json")
@ID(ReqId.ID_1) //唯一ID, ID 是自定义注解
suspend fun banner(): Response<WanAndroidBanner> //Response 是自定义类型NetworkResponseCall的别名
}
class WanAndroidRepository : NetworkRepository<WanAndroidService>(
WanAndroidService::class.java,
baseUrl = "https://wanandroid.com/"
) {
suspend fun cropBanner() = service.cropBanner()
}
viewModelScope.launch {//协成启动supend方法
WanAndroidRepository()
.cropBanner()
.onFailure { error ->
_uiState.update { it.copy(name = "${error.status} ${error.id}") }
}.onSuccess { banners ->
_uiState.update { it.copy(name = banners.firstOrNull()?.title ?: "empty") }
}
}
网络请求公共参数
Network.networkParameterAdapter = object : NetworkParameterAdapter {
//GET 参数
override fun getGetParameter(request: Request): Map<String, String> = mutableMapOf()
//POST url 参数 (url?key=value 项目中总是有奇奇怪怪的需求)
override fun getPostQueryParameter(request: Request): Map<String, String> = mutableMapOf()
//POST Formbody 参数
override fun getPostFieldParameter(request: Request): Map<String, String> = mutableMapOf()
}
统一接口返回回调
Network.responseListener.add {
val error = when (it) {
is NetworkResponse.Success -> "Success"
is NetworkResponse.BizError -> "BizError ${it.msg}"
is NetworkResponse.ApiError -> "ApiError"
is NetworkResponse.NetworkError -> "NetworkError ${it.error.message}"
is NetworkResponse.UnknownError -> "UnknownError ${it.error?.message}"
}
println("response listener $error")
}
三、实现过程
Retrofit Call Adapter
通过扩展 Retrofit Call Adapter 实现返回类型的包装,统一接口返回回调和接口唯一ID功能。
class NetworkResponseAdapterFactory(
private val responseListener: ((response: NetworkResponse<Any, Any>) -> Unit)? = null
) : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): CallAdapter<*, *>? {
//检查是否是要处理的类型,不是返回null, 是返回 NetworkResponseAdapter
if (getRawType(responseType) != NetworkResponse::class.java) return null
return NetworkResponseAdapter<Any, Any>(responseType, errorBodyConverter, responseListener, id)
}
}
class NetworkResponseAdapter<S : Any, E : Any>(
private val successType: Type,
private val errorBodyConverter: Converter<ResponseBody, E>,
private val responseListener: ((response: NetworkResponse<Any, Any>) -> Unit)? = null,
private val id: String = ""
) : CallAdapter<NetworkResponse<S, E>, Call<NetworkResponse<S, E>>> {
//Converter 序列化的类型
override fun responseType(): Type = successType
override fun adapt(call: Call<NetworkResponse<S, E>>): Call<NetworkResponse<S, E>> {
//返回自定义的call
return NetworkResponseCall(call, errorBodyConverter, responseListener, id)
}
}
//包装数据返回给接口调用处
internal class NetworkResponseCall<S : Any, E : Any>(
private val delegate: Call<NetworkResponse<S, E>>,
private val errorConverter: Converter<ResponseBody, E>,
private val responseListener: ((response: NetworkResponse<Any, Any>) -> Unit)? = null,
private val id: String = ""
) : Call<NetworkResponse<S, E>> {
override fun enqueue(callback: Callback<NetworkResponse<S, E>>) {
return delegate.enqueue(object : Callback<NetworkResponse<S, E>> {
override fun onResponse(
call: Call<NetworkResponse<S, E>>,
response: Response<NetworkResponse<S, E>>
) {
//省略处理代码
//Moshi 解析后再转成包装类型 NetworkResponse 返回
callback.onResponse(
this@NetworkResponseCall,
Response.success(it.apply {
responseListener?.invoke(this)
})
}
})
}
}
//网络请求返回的包装类,含有结果&异常信息
sealed class NetworkResponse<out T : Any, out U : Any>(open val id: String = "") {
data class Success<T : Any>(val data: T, override val id: String = "") : NetworkResponse<T, Nothing>()
data class BizError(val code: Int, val msg: String, override val id: String = "") : NetworkResponse<Nothing, Nothing>()NetworkResponse<Nothing, Nothing>()
//省略其他类型
}
网络请求公共参数
使用Okhttp Interceptor 实现公共参数的功能。
class NetworkParameterInterceptor(private val networkParameterAdapter: NetworkParameterAdapter) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val urlBuilder = request.url.newBuilder()
when (request.method) {
"GET" -> {
val parameter = networkParameterAdapter.getGetParameter(request)
parameter.forEach {
urlBuilder.addQueryParameter(it.key, it.value)
}
request.newBuilder().url(urlBuilder.build()).build()
}
"POST" -> {
val queryParameter = networkParameterAdapter.getPostQueryParameter(request)
queryParameter.forEach {
urlBuilder.addQueryParameter(it.key, it.value)
}
request = request.newBuilder().url(urlBuilder.build()).build()
if (request.body is FormBody) {
val builder = FormBody.Builder()
val body = request.body as FormBody
for (i in 0 until body.size) {
builder.add(body.encodedName(i), body.encodedValue(i))
}
val fieldParameter = networkParameterAdapter.getPostFieldParameter(request)
fieldParameter.forEach {
builder.add(it.key, it.value)
}
request = request.newBuilder().post(builder.build()).build()
}
}
}
return chain.proceed(request)
}
}
去信封功能
自定义Moshi json 解析器,Moshi 解析完成后返回给 NetworkResponseCall 处理。
//Moshi JsonAdapter.Factory
class NetworkMoshiAdapterFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
if (Utils.getRawType(type) != NetworkResponse::class.java) return null
val responseType = getParameterUpperBound(0, type as ParameterizedType)
val dataTypeAdapter = moshi.nextAdapter<Any>(
this, responseType, annotations
)
return NetworkCropResponseTypeAdapter(responseType, dataTypeAdapter)
}
}
//Moshi 去信封的 JsonAdapter
class NetworkCropResponseTypeAdapter<T>(
private val outerType: Type,
private val dataTypeAdapter: JsonAdapter<T>
) : JsonAdapter<T>() {
override fun fromJson(reader: JsonReader): T? {
reader.beginObject()
var data: Any? = null
var errorCode = 0
var errorMsg = ""
var dataException: Exception? = null
while (reader.hasNext()) {
when (reader.nextName()) {
"data" -> data = dataTypeAdapter.fromJson(reader)
"errorCode" -> errorCode = reader.nextInt()
"errorMsg" -> errorMsg = reader.nextString()
else -> reader.skipValue()
}
}
reader.endObject()
//省略处理代码
return NetworkResponse.Success(data) as? T
}
}
样例代码
参考资料
# Create Retrofit CallAdapter for Coroutines to handle response as states
网友评论