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
}
})
-
为每个http请求添加公共参数,如channel、apiVersion、imei..
利用OkHttp提供的拦截器对将要发出的请求进行拦截;
BasicParamsInterceptor
实现了okhttp3.Interceptor
接口, 我们只需在初始化OkHttpClient时addInterceptor
添加拦截器,并将公共参数添加进BasicParamsInterceptor
;
Github地址 https://github.com/jkyeo/okhttp-basicparamsinterceptor -
数据解析和统一异常处理
一般后台给的响应数据都有一个统一格式,包含响应码、提示和具体数据,如
{
"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())
}
}
上面这两个类基本没做什么改动,直接照搬GsonConverterFactory
与GsonRequestBodyConverter
,我们主要改动的是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)
}
})