美文网首页
Android 网络功能搭建 (协成 + Retrofit +

Android 网络功能搭建 (协成 + Retrofit +

作者: 毛肥兔子 | 来源:发表于2022-03-11 11:06 被阅读0次

概述

网络层功能搭建,使用技术: 协成 + Retrofit + Okhttp + Moshi。

一、支持功能

  1. 实现优雅调用网络请求:支持多Domain。

  2. 网络请求公共参数:GET 参数、POST url 参数、POST Formbody 参数。

  3. 网络请求唯一ID:请求时带入,完成后带回。方便测试、bug统计。

  4. 统一接口返回回调:统一处理错误码,log打点统计。

  5. 去信封功能:接口返回如下的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
    }
}

样例代码

# AF Github

参考资料

# Kotlin 协程与 Retrofit

# Create Retrofit CallAdapter for Coroutines to handle response as states

相关文章

网友评论

      本文标题:Android 网络功能搭建 (协成 + Retrofit +

      本文链接:https://www.haomeiwen.com/subject/fqpcdrtx.html