概述
網(wǎng)絡(luò)層功能搭建孤紧,使用技術(shù): 協(xié)成 + Retrofit + Okhttp + Moshi筛圆。
一煮落、支持功能
實現(xiàn)優(yōu)雅調(diào)用網(wǎng)絡(luò)請求:支持多Domain。
網(wǎng)絡(luò)請求公共參數(shù):GET 參數(shù)怯邪、POST url 參數(shù)骂远、POST Formbody 參數(shù)趣避。
網(wǎng)絡(luò)請求唯一ID:請求時帶入尸执,完成后帶回。方便測試浸卦、bug統(tǒng)計署鸡。
統(tǒng)一接口返回回調(diào):統(tǒng)一處理錯誤碼,log打點統(tǒng)計。
去信封功能:接口返回如下的json格式靴庆, 直接解析成data對應(yīng)的model时捌。
{
"errorCode": 0,
"errorMsg": "",
"data": {
"title": "this is title"
}
}
二、使用方式
網(wǎng)絡(luò)請求事例
@JsonClass(generateAdapter = true)
@CropEnvelope //CropEnvelope注解即可實現(xiàn)去信封功能 (前提是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 {//協(xié)成啟動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") }
}
}
網(wǎng)絡(luò)請求公共參數(shù)
Network.networkParameterAdapter = object : NetworkParameterAdapter {
//GET 參數(shù)
override fun getGetParameter(request: Request): Map<String, String> = mutableMapOf()
//POST url 參數(shù) (url?key=value 項目中總是有奇奇怪怪的需求)
override fun getPostQueryParameter(request: Request): Map<String, String> = mutableMapOf()
//POST Formbody 參數(shù)
override fun getPostFieldParameter(request: Request): Map<String, String> = mutableMapOf()
}
統(tǒng)一接口返回回調(diào)
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")
}
三匣椰、實現(xiàn)過程
Retrofit Call Adapter
通過擴展 Retrofit Call Adapter 實現(xiàn)返回類型的包裝,統(tǒng)一接口返回回調(diào)和接口唯一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)
}
}
//包裝數(shù)據(jù)返回給接口調(diào)用處
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 解析后再轉(zhuǎn)成包裝類型 NetworkResponse 返回
callback.onResponse(
this@NetworkResponseCall,
Response.success(it.apply {
responseListener?.invoke(this)
})
}
})
}
}
//網(wǎng)絡(luò)請求返回的包裝類,含有結(jié)果&異常信息
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>()
//省略其他類型
}
網(wǎng)絡(luò)請求公共參數(shù)
使用Okhttp Interceptor 實現(xiàn)公共參數(shù)的功能蛤奥。
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