Retrofit 2.0的封装与异常处理

Retrofit 2.0的基本使用,网上教程很多,Retrofit 官方教程

  • 定义接口
    interface ApiService {
        @POST("market/getMarket")
        fun getMarket(): Call<HttpResponse<Market>>
    }
  • 初始化Retrofit
    const val URL_BASE = "http://192.168.1.1:9200/myproject/api/"
    
    class RetrofitClient private constructor() {
        private var apiService: ApiService
        init {
            val mRetrofit = Retrofit.Builder()
                    .baseUrl(URL_BASE)
                    .client(getOkHttpClient())
                     /** 添加自定义的Gson解析 */
                    .addConverterFactory(CustomGsonConverterFactory.create())
                    .build()
            apiService = mRetrofit.create(ApiService::class.java)
        }
    
        companion object {
            fun getInstance(): RetrofitClient {
                return Inner.instance
            }
        }
    
        private object Inner {
            val instance = RetrofitClient()
        }
    
        private fun getOkHttpClient(): OkHttpClient {
            val builder = OkHttpClient.Builder()
            builder.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            /** 为请求添加公共参数 */
            builder.addInterceptor(BasicParamsInterceptor.Builder()
                    .addParam("channel", "googlePlay").addParam("apiVer", "15").build())
            builder.addInterceptor(BasicParamsInterceptor.Builder().build())
            builder.connectTimeout(20, TimeUnit.SECONDS)
            builder.writeTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS)
            builder.retryOnConnectionFailure(true)
            return builder.build()
        }
    
        fun getAPI(): ApiService {
            return Companion.getInstance().apiService
        }
    }
  • 调用接口
    RetrofitClient.getInstance().getAPI().getMarket().enqueue(object : Callback<HttpResponse<Market>> {
        override fun onResponse(call:Call<HttpResponse<Market>>,response:Response<HttpResponse<Market>>{
            //TODO on success response
        }
        override fun onFailure(call: Call<HttpResponse<Market>>, throwable: Throwable) {
            //TODO on failures response         
        }
    })
  1. 为每个http请求添加公共参数,如channel、apiVersion、imei..

    利用OkHttp提供的拦截器对将要发出的请求进行拦截;
    BasicParamsInterceptor实现了okhttp3.Interceptor接口, 我们只需在初始化OkHttpClient时addInterceptor添加拦截器,并将公共参数添加进BasicParamsInterceptor;
    Github地址 https://github.com/jkyeo/okhttp-basicparamsinterceptor

  2. 数据解析和统一异常处理

    一般后台给的响应数据都有一个统一格式,包含响应码、提示和具体数据,如

  {
    "code":0,
    "msg":"success",
    "data":{
        "id":1212,
        "name":"MyName"
    }
  } 

如果服务端数据响应成功,这里的code为0,msg为success;
如果服务器端出现错误或者用户操作失败,code为对应错误码,msg为对应提示信息,如"服务器忙,请稍后再试"、"用户权限不足"等;
注意,这种code不为0的情况属于服务端主动提示的异常,但整个http请求与响应过程是没有问题的,所以这种情况下是不会走Callback的onFailure回调的。

  • 定义响应model
    open class HttpStatus {
        var msg: String? = null
        var code: Int = -1
        fun isSuccess(): Boolean {
            return code == 0
        }
    }
    class HttpResponse<T> : HttpStatus() {
        var data: T? = null
    }

retrofit已经为我们封装好了converter-gson,只需在gradle中添加依赖implementation 'com.squareup.retrofit2:converter-gson:2.4.0',然后Retrofit初始化时addConverterFactory添加GsonConverterFactory 响应即可由Gson进行处理。

  • 定义一个Exception代表服务端异常
class ApiException(var code: Int, override var message: String?) : RuntimeException(message) {
    fun code(): Int {
        return code
    }
    fun message(): String? {
        return message
    }
}
  • 参照GsonConverterFactory,自定义一个CustomGsonConverterFactory,添加统一异常处理功能。
    class CustomGsonConverterFactory(val gson: Gson) : Converter.Factory() {
       companion object {
            fun create(): CustomGsonConverterFactory {
                return create(Gson())
            }
            private fun create(gson: Gson?): CustomGsonConverterFactory {
                if (gson == null) throw NullPointerException("gson == null")
                return CustomGsonConverterFactory(gson)
            }
        }

        override fun responseBodyConverter(type: Type?, annotations: Array<out Annotation>?, retrofit:Retrofit?):Converter<ResponseBody, *>? {
            return CustomGsonResponseBodyConverter(gson, gson.getAdapter(TypeToken.get(type)))
        }

        override fun requestBodyConverter(type: Type?, parameterAnnotations: Array<out Annotation>?,methodAnnotations: Array<out Annotation>?, retrofit: Retrofit?): Converter<*, RequestBody>? {
            return CustomGsonRequestBodyConverter(gson, gson.getAdapter(TypeToken.get(type)))
        }
  }
    class CustomGsonRequestBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<T, RequestBody> {
        private val MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8")
        private val UTF_8 = Charset.forName("UTF-8")
    
        override fun convert(value: T): RequestBody {
            val buffer = Buffer()
            val writer = OutputStreamWriter(buffer.outputStream(), UTF_8)
            val jsonWriter = gson.newJsonWriter(writer)
            adapter.write(jsonWriter, value)
            jsonWriter.close()
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString())
        }
    }

上面这两个类基本没做什么改动,直接照搬GsonConverterFactoryGsonRequestBodyConverter,我们主要改动的是GsonResponseBodyConverter , 具体说明见注释

class CustomGsonResponseBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<ResponseBody, T> {
    override fun convert(value: ResponseBody): T {
        val response = value.string()
        val httpStatus = gson.fromJson(response, HttpStatus::class.java)
        /** 先将code与msg解析出来,code非0的情况下直接抛ApiException异常,这样我们就将这种异常交给onFailure()处理了**/
        if (!httpStatus.isSuccess()) {
            value.close()
            throw ApiException(httpStatus.code, httpStatus.msg)
        }
        val contentType = value.contentType()
        val charset = contentType?.charset(UTF_8) ?: UTF_8
        val inputStream = ByteArrayInputStream(response.toByteArray())
        val reader = InputStreamReader(inputStream, charset!!)
        val jsonReader = gson.newJsonReader(reader)

        value.use { _ ->
            val result = adapter.read(jsonReader)
            if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
                throw JsonIOException("JSON document was not fully consumed.")
            }
            return result
        }
    }
}

上面工作完成后,再写个解析异常的帮助类,也可以选择进一步向上封装。

object HttpResponseProcess {
    private const val NO_CONNECTION = 3001
    private const val CONNECTION_TIMEOUT = 3002
    private const val CONNECTION_REFUSED = 3003
    private const val STATUS_EXCEPTION = 3004
    private const val JSON_EXCEPTION = 3005
    private const val PERMISSION_DENIED = 3006
    private const val HTTPS_HANDSHAKE_FAILED = 3007
    private const val API_ERROR = 3008
    private const val UNKNOWN = 3009

    fun <R> responseProcess(response: Response<HttpResponse<R>>): HttpResponse<R> {
        var httpResponse: HttpResponse<R> = HttpResponse()
        if (response.isSuccessful) {
            if (response.body() == null || response.code() == 204) {
                httpResponse.code = STATUS_EXCEPTION
                httpResponse.msg = String.format(MyApplication.getAppContext().getString(R.string.netError_httpResponseError), response.code())
            } else {
                httpResponse = response.body() as HttpResponse<R>
            }
        } else {
            httpResponse.code = STATUS_EXCEPTION
            httpResponse.msg = String.format(MyApplication.getAppContext().getString(R.string.netError_httpResponseError), response.code())
        }
        return httpResponse
    }

    fun <R> responseProcess(throwable: Throwable): HttpResponse<R> {
        val context = MyApplication.getAppContext()
        val httpResponse: HttpResponse<R> = HttpResponse()
        when (throwable) {
            is HttpException -> {
                httpResponse.code = STATUS_EXCEPTION
                httpResponse.msg = String.format(context.getString(R.string.netError_httpResponseError), throwable.code())
            }
            is SocketTimeoutException -> {
                httpResponse.code = CONNECTION_TIMEOUT
                httpResponse.msg = context.getString(R.string.netError_connectionTimeout)
            }
            is SSLHandshakeException -> {
                httpResponse.code = HTTPS_HANDSHAKE_FAILED
                httpResponse.msg = String.format(context.getString(R.string.netError_httpsHandshakeFailed))
            }
            is JSONException, is JsonParseException, is ParseException -> {
                httpResponse.code = JSON_EXCEPTION
                httpResponse.msg = String.format(context.getString(R.string.netError_parseJsonException), throwable.message)
            }
            is UnknownHostException, is ConnectException, is SocketException -> {
                val causeMessage = throwable.message
                if (causeMessage != null && causeMessage.contains("Permission denied")) {
                    httpResponse.code = PERMISSION_DENIED
                    httpResponse.msg = String.format(context.getString(R.string.netError_connectionDenied), context.getString(R.string.app_name))
                } else if (causeMessage != null && causeMessage.contains("Connection refused")) {
                    httpResponse.code = CONNECTION_REFUSED
                    httpResponse.msg = context.getString(R.string.netError_connectionRefused)
                } else {
                    httpResponse.code = NO_CONNECTION
                    httpResponse.msg = context.getString(R.string.netError_noConnection)
                }
            }
            is ApiException -> {
                httpResponse.code = API_ERROR
                httpResponse.msg = String.format(context.getString(R.string.netError_apiException), throwable.message())
            }
            else -> {
                httpResponse.code = UNKNOWN
                httpResponse.msg = String.format(context.getString(R.string.netError_unknown), throwable.message)
            }
        }
        return httpResponse
    }
}

HttpResponseProcess中定义了多种异常类型和对应的代码,比如无连接3001,连接超时3002;两个responseProcess方法分别用于处理retrofit2.Callback的onResponse与onFailure的异常。

 RetrofitClient.getInstance().getAPI().getMarket().enqueue(object : Callback<HttpResponse<Market>> {
            override fun onResponse(call: Call<HttpResponse<Market>>, response: Response<HttpResponse<Market>>) {
                val httpResponse = HttpResponseProcess.responseProcess(response)
            }
            override fun onFailure(call: Call<HttpResponse<Market>>, throwable: Throwable) {
                val errorResponse = HttpResponseProcess.responseProcess<Market>(throwable)
            }
        })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。