原始的retrofit使用方式
//创建网络请求接口对象实例
Api api = mRetrofit.create(Api.class);
//对发送请求进行封装
Call<Data<Info>> dataCall = api.getJsonData("xxx", "xx");
//异步请求
dataCall.enqueue(new Callback<Data<Info>>() {
//请求成功回调
@Override
public void onResponse(Call<Data<Info>> call, Response<Data<Info>> response) {
}
//请求失败回调
@Override
public void onFailure(Call<Data<Info>> call, Throwable t) {
}
});
//同步请求
Response<Data<Info>> data= dataCall.execute();
封装 RetrofitFlow.kt
- injectApi 封装获取retrofit实例的方法
inline fun <reified T : Any> injectApi(mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED): Lazy<T> {
return lazy(mode) {
RetrofitFlow.getRetrofit().create(T::class.java)
}
}
private val bapiLoginApi by injectApi<PrivateLoginApi>()
@JvmStatic
fun getRetrofit(): Retrofit {
if (retrofitInstance == null) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val mGlobalRetrofit = Retrofit.Builder()
.client(OkHttpClient.Builder().addInterceptor(loggingInterceptor).build())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("todo ")
.build()
retrofitInstance = mGlobalRetrofit.newBuilder().client(
OkHttpClient.Builder().addInterceptor(loggingInterceptor).build().newBuilder()
.addNetworkInterceptor(loggingInterceptor).build()
).build()
}
return retrofitInstance!!
}
- requestFlow 发起请求并emit结果
inline fun <reified T : Any, reified R : BaseApi> R.requestFlow(
crossinline request: suspend R.() -> Response<BapiResponse<T>>,
): Flow<T> {
return flow {
var urlPath: String? = null
try {
val response = request()
urlPath = try {
response.raw().request().url().encodedPath()
} catch (e: java.lang.Exception) {
Log.e("retro-flow", "get request url failed", e)
null
}
val bapiRes = response.body()
if (response.code() == 200 && bapiRes != null && bapiRes.errorCode == 10000 && bapiRes.data != null) {
emit(bapiRes.data)
} else {
if (response.code() == 200) {
throw ApiException(
bapiRes?.errorCode ?: response.code(),
bapiRes?.errorMsg ?: response.message() ?: "response error"
)
} else {
throw HttpException()
}
}
} catch (e: Exception) {
if (e is ApiException) {
throw e
} else {
throw HttpException()
}
}
}.flowOn(Dispatchers.IO).onCompletion { cause ->
run {
cause?.let {
Log.e("retro-flow", "request error", it)
}
}
}
}
- next collect请求结果
suspend inline fun <T> Flow<T>.next(crossinline bloc: suspend T.() -> Unit): Unit =
catch { }.collect { bloc(it) }
- 具体使用
确保回调在当前组件的生命周期内,防止内存泄漏
- 源码
package com.example.stateflow
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitFlow {
companion object {
private var retrofitInstance: Retrofit? = null
@JvmStatic
fun getRetrofit(): Retrofit {
if (retrofitInstance == null) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val mGlobalRetrofit = Retrofit.Builder()
.client(OkHttpClient.Builder().addInterceptor(loggingInterceptor).build())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("todo ")
.build()
retrofitInstance = mGlobalRetrofit.newBuilder().client(
OkHttpClient.Builder().addInterceptor(loggingInterceptor).build().newBuilder()
.addNetworkInterceptor(loggingInterceptor).build()
).build()
}
return retrofitInstance!!
}
@JvmStatic
fun updateBaseUrl(baseUrl: String?) {
baseUrl ?: return
val old = retrofitInstance
if (old != null) {
retrofitInstance = old.newBuilder().baseUrl(baseUrl).build()
}
}
}
}
inline fun <reified T : Any> injectApi(mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED): Lazy<T> {
return lazy(mode) {
RetrofitFlow.getRetrofit().create(T::class.java)
}
}
suspend inline fun <T> Flow<T>.next(crossinline bloc: suspend T.() -> Unit): Unit =
catch { }.collect { bloc(it) }
fun <T> Flow<T>.catchError(bloc: Throwable.() -> Unit) = catch { cause -> bloc(cause) }
inline fun <reified T : Any, reified R : BaseApi> R.requestFlow(
crossinline request: suspend R.() -> Response<BapiResponse<T>>,
): Flow<T> {
return flow {
var urlPath: String? = null
try {
val response = request()
urlPath = try {
response.raw().request().url().encodedPath()
} catch (e: java.lang.Exception) {
Log.e("retro-flow", "get request url failed", e)
null
}
val bapiRes = response.body()
if (response.code() == 200 && bapiRes != null && bapiRes.errorCode == 10000 && bapiRes.data != null) {
emit(bapiRes.data)
} else {
if (response.code() == 200) {
throw ApiException(
bapiRes?.errorCode ?: response.code(),
bapiRes?.errorMsg ?: response.message() ?: "response error"
)
} else {
throw HttpException()
}
}
} catch (e: Exception) {
if (e is ApiException) {
throw e
} else {
throw HttpException()
}
}
}.flowOn(Dispatchers.IO).onCompletion { cause ->
run {
cause?.let {
Log.e("retro-flow", "request error", it)
}
}
}
}
inline fun <reified T : Any, reified R : BaseApi> R.requestFlowNotBapi(
crossinline request: suspend R.() -> Response<T>,
): Flow<T> {
return flow {
var urlPath: String? = null
try {
val response = request()
urlPath = try {
response.raw().request().url().encodedPath()
} catch (e: java.lang.Exception) {
Log.e("retro-flow", "get request url failed", e)
null
}
val baseRes = response.body()
if (response.code() == 200 && baseRes != null) {
emit(baseRes)
} else {
throw ApiException(response.code(), response.message() ?: "response error")
}
} catch (e: Exception) {
}
}.flowOn(Dispatchers.IO).onCompletion { cause ->
run {
cause?.let {
Log.e("retro-flow", "request error", it)
}
}
}
}
class ApiException(val code: Int, val msg: String) : Exception(msg)
class HttpException() : Exception()
interface BaseApi {}
data class BapiResponse<T>(
val errno: Int? = null,
val errmsg: String? = null,
val data: T? = null
) {
val errorCode: Int?
get() {
return errno
}
val errorMsg: String?
get() {
return errmsg
}
}