Retrofit 的基本用法
一、添加依赖和网络权限
添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 可选
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
- 第一条依赖是下载Retrofit、OkHttp和Okio这几个库,我们就不需要手动引入OkHttp库了;
- 第二条依赖是一个Retrofit的转换库,它是借助GSON来解析JSON数据的,所以也会将GSON库一起下载;
- 第三条是 okHttp 的日志拦截器相关,可选。
添加网络权限
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
二、Retrofit data api 部分封装
2.1 创建接收服务器返回数据类、异常类、异常码
通用返回数据类
import com.google.gson.annotations.SerializedName
data class CommonResponse<T>(
@SerializedName("code") val code: Int = -1,
@SerializedName("data") val data: T,
@SerializedName("msg") val msg: String
) {
fun isSuccess(): Boolean {
return code == 0
}
}
异常类
data class ResponseException(
var code: Int, override var message: String?
) : RuntimeException(message)
异常码定义
import androidx.annotation.IntDef
@IntDef(
ErrorCode.SUCCESS, // 成功
ErrorCode.FAIL, // 失败
ErrorCode.NETWORK_EXCEPTION,//无网络,网络连接异常
ErrorCode.HOST_ERROR,//host异常
ErrorCode.TIMEOUT,//超时
ErrorCode.CANCEL,//取消
ErrorCode.JSON_SYNTAX_EXCEPTION,//数据解析异常
ErrorCode.OK,//请求正常
ErrorCode.CREATED,
ErrorCode.FORBIDDEN,
ErrorCode.UNAUTHORIZED,//无授权
ErrorCode.NOT_FOUND,
ErrorCode.OTHER,//其他错误,目前还未关注和处理的
ErrorCode.CUSTOM_FIRST,//自定义,可自行修改
ErrorCode.VALUE_IS_NULL//空值
)
@Retention(AnnotationRetention.SOURCE)
annotation class ErrorCode {
companion object {
const val SUCCESS = 0
const val FAIL = 1
const val NETWORK_EXCEPTION = 2
const val HOST_ERROR = 3
const val TIMEOUT = 4
const val CANCEL = 5
const val JSON_SYNTAX_EXCEPTION = 6
const val OK = 200
const val CREATED = 201
const val FORBIDDEN = 401
const val UNAUTHORIZED = 402
const val NOT_FOUND = 404
const val OTHER = 509
const val CUSTOM_FIRST = 600
const val VALUE_IS_NULL = CUSTOM_FIRST + 1
}
}
2.2 封装异常处理
import com.google.gson.JsonSyntaxException
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
private const val TAG = "getResultOrNull"
suspend fun <T> getResultOrNull(block: suspend () -> CommonResponse<T>): T? {
runCatching {
block()
}.onSuccess {
return it.data
}.onFailure {
when (it) {
is ResponseException -> {
logW(TAG, "getResult exception, code: ${it.code} message: ${it.message}")
}
is UnknownHostException,
is HttpException,
is ConnectException,
is SocketTimeoutException,
is SocketException,
is NumberFormatException,
is IllegalArgumentException,
is IllegalStateException,
is JsonSyntaxException -> {
logW(TAG, "getResult exception: ${it.message}")
}
else -> {
logW(TAG, "getResult other exception: ${it.message}")
}
}
return null
}
return null
}
2.3 定义 SSLSocketClient
import java.security.KeyManagementException
import java.security.KeyStore
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.*
object SSLSocketClient {
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
fun getSSLSocketFactory(): SSLSocketFactory {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, getTrustManager(), SecureRandom())
return sslContext.socketFactory
}
private fun getTrustManager(): Array<TrustManager> {
val trustManager: X509TrustManager = object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
return arrayOf(trustManager)
}
fun getHostnameVerifier(): HostnameVerifier {
return HostnameVerifier { _, _ -> true }
}
@Throws(Exception::class)
fun getX509TrustManager(): X509TrustManager {
var trustManager: TrustManager? = null
val trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as? KeyStore)
val trustManagers = trustManagerFactory.trustManagers
if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
throw IllegalStateException("Unexpected default trust managers: $trustManagers")
}
return trustManagers[0] as X509TrustManager
}
}
2.4 自定义 CustomGsonConverterFactory
import com.google.gson.Gson
import com.google.gson.JsonIOException
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonToken
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import okio.Buffer
import retrofit2.Converter
import retrofit2.Retrofit
import java.io.ByteArrayInputStream
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.lang.reflect.Type
import java.nio.charset.Charset
import kotlin.text.Charsets.UTF_8
class CustomGsonConverterFactory private constructor(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)))
}
}
private class CustomGsonRequestBodyConverter<T>(private val gson: Gson, private val adapter: TypeAdapter<T>) : Converter<T, RequestBody> {
private val MEDIA_TYPE = "application/json; charset=UTF-8".toMediaTypeOrNull()
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 buffer.readByteString().toRequestBody(MEDIA_TYPE)
}
}
private 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 commonResponse = gson.fromJson(response, CommonResponse::class.java)
/** 先将code与msg解析出来,code非0的情况下直接抛ApiException异常,这样我们就将这种异常交给onFailure()处理了**/
if (!commonResponse.isSuccess()) {
value.close()
throw ResponseException(commonResponse.code, commonResponse.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
}
}
}
2.5 创建 RetrofitClient 以及业务接口
object RetrofitClient {
private val instance: Retrofit by lazy {
val logger = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC }
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.addInterceptor(Interceptor { chain ->
val originalRequest: Request = chain.request()
val request = originalRequest.newBuilder()
.header("content-type", "application/json;charset:utf-8")
.build()
chain.proceed(request)
})
.sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.getX509TrustManager())
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
Retrofit.Builder().baseUrl(HttpUrls.getBaseUrl())
.client(client)
.addConverterFactory(CustomGsonConverterFactory.create())
.build()
}
// TestService 见下
fun getTestService(): TestService {
return instance.create(TestService::class.java)
}
}
三、具体业务接口封装
3.1 data 部分定义
IDataSource
interface IDataSource {
suspend fun getPrivacyList(): List<PrivacyInfo>
}
TestService
interface TestService {
@POST("privacy/getPrivacyList")
suspend fun getPrivacyList(@Body req: GetPrivacyReq): CommonResponse<List<PrivacyInfo>>
}
RemoteDataSource
class RemoteDataSource private constructor() : IDataSource {
private lateinit var testService: TestService
companion object {
@Volatile
private var instance: RemoteDataSource? = null
fun getInstance(context: Context): RemoteDataSource {
return instance ?: synchronized(this) {
instance ?: RemoteDataSource().also {
it.testService = RetrofitClient.getTestService()
instance = it
}
}
}
}
override suspend fun getPrivacyList(): List<PrivacyInfo> = withContext(Dispatchers.IO) {
val req = buildGetPrivacyReq() // 创建入参
val result = getResultOrNull {
testService.getPrivacyList(req)
}
result?: mutableListOf()
}
}
TestRepository
object TestRepository {
private val localDataSource: LocalDataSource by lazy {
LocalDataSource.getInstance(App.appContext)
}
private val remoteDataSource: RemoteDataSource by lazy {
RemoteDataSource.getInstance(App.appContext)
}
suspend fun getPrivacyList(): List<PrivacyInfo> {
return remoteDataSource.getPrivacyList()
}
}