美文网首页
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