Retrofit LiveDataCallAdapterFact
2021-09-09 本文已影响0人
猿规
retrofit可以访问网络后自动解析成对象,很丝滑,而要使用这个功能只需要 addConverterFactory(GsonConverterFactory.create()) ,添加了一个gson解析工厂,又或者搭配rxjava的 RxJava2CallAdapterFactory 解析工厂。
在接口成功时解析成 Observable 对象返回,现如今流行mvvm模式,使用 livedata 操作数据的越来越多,而且还有Flow,rxjava已经开始虚了。
这里主要讲讲 livedata跟retrofit 结合处理解析。
在这种情况下,livedata也能跟rxjava一样,直接通过添加一个解析工厂然后返回一个livedata对象数据结果给我们。
以前接口返回结果,可以配置成 Observable对象,现在如果要换成 livedata 对象,就需要自定义一个工厂类。
class LiveDataCallAdapterFactory : Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): CallAdapter<*, *>? {
/** ParameterizedType(参数化类型)即泛型;例如:List< T>、Map< K,V>等带有参数化的对象 */
val selfType = getRawType(returnType)
if (selfType != LiveData::class.java) {
LogUtils.e("Response must be LiveData.class. error: type is $selfType")
throw IllegalStateException("return type must be LiveData.class. error: type is $selfType")
}
val observableType = getParameterUpperBound(0, returnType as ParameterizedType)
val rawObservableType = getRawType(observableType)
if (rawObservableType != ApiResponse::class.java) {
throw IllegalArgumentException("type must be a resource")
}
if (observableType !is ParameterizedType) {
throw IllegalArgumentException("resource must be parameterized")
}
val bodyType = getParameterUpperBound(0, observableType)
return LiveDataCallAdapter<Any>(
bodyType
)
}
}
此处返回时检查数据类型,看有没有正确的解析成 livedata 对象,如果有的话进入工厂,工厂里面直接返回结果对象。
class LiveDataCallAdapter<T>(private val responseType: Type) :
CallAdapter<T, LiveData<ApiResponse<T>>> {
override fun responseType() = responseType
override fun adapt(call: Call<T>): LiveData<ApiResponse<T>> {
return object : LiveData<ApiResponse<T>>() {
/** 确保多线程的情况下安全的运行,不会被其它线程打断,一直等到该方法执行完成,才由JVM从等待队列中选择其它线程进入 */
private var started = AtomicBoolean(false)
//处于active状态的观察者(observe)个数从0变为1,回调LiveData的onActive()方法
override fun onActive() {
super.onActive()
//把当前对象值与expect相比较,如果相等,把对象值设置为update,并返回为true
if (started.compareAndSet(false, true)) {
call.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
postValue(
ApiResponse.create<T>(
response
)
)
}
override fun onFailure(call: Call<T>, throwable: Throwable) {
postValue(
ApiResponse.create<T>(
throwable
)
)
}
})
}
}
}
}
}
主要解析逻辑其实就是解析对象 ApiResponse ,里面可以自己定义一些规则。
/**
* Common class used by API responses.
* @param <T> the type of the response object
</T> */
@Suppress("unused") // T is used in extending classes
sealed class ApiResponse<T> {
companion object {
fun <T> create(error: Throwable): ApiErrorResponse<T> {
return ApiErrorResponse(
error.message ?: "unknown error"
)
}
fun <T> create(response: Response<T>): ApiResponse<T> {
return if (response.isSuccessful) {
val body = response.body()
if (body == null || response.code() == 204) {
ApiEmptyResponse()
} else {
ApiSuccessResponse(
body = body,
linkHeader = response.headers()["link"]
)
}
} else {
val msg = response.errorBody()?.string()
val errorMsg = if (msg.isNullOrEmpty()) {
response.message()
} else {
msg
}
ApiErrorResponse(
errorMsg ?: "unknown error"
)
}
}
}
}
/**
* separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
*/
class ApiEmptyResponse<T> : ApiResponse<T>()
data class ApiSuccessResponse<T>(
val body: T,
val links: Map<String?, String?>
) : ApiResponse<T>() {
constructor(body: T, linkHeader: String?) : this(
body = body,
links = linkHeader?.extractLinks() ?: emptyMap()
)
val nextPage: Int? by lazy(LazyThreadSafetyMode.NONE) {
links[NEXT_LINK]?.let { next ->
val matcher = PAGE_PATTERN.matcher(next)
if (!matcher.find() || matcher.groupCount() != 1) {
null
} else {
try {
Integer.parseInt(matcher.group(1))
} catch (ex: NumberFormatException) {
Log.w("ApiResponse", "cannot parse next page from $next")
null
}
}
}
}
companion object {
private val LINK_PATTERN = Pattern.compile("<([^>]*)>[\\s]*;[\\s]*rel=\"([a-zA-Z0-9]+)\"")
private val PAGE_PATTERN = Pattern.compile("\\bpage=(\\d+)")
private const val NEXT_LINK = "next"
private fun String.extractLinks(): Map<String?, String?> {
val links = mutableMapOf<String?, String?>()
val matcher = LINK_PATTERN.matcher(this)
while (matcher.find()) {
val count = matcher.groupCount()
if (count == 2) {
links[matcher.group(2)] = matcher.group(1)
}
}
return links
}
}
}
data class ApiErrorResponse<T>(val errorMessage: String) : ApiResponse<T>()
成功回调返回success,不同的状态可以定义不同的模型,因为是直接返回的livedata类型,所以你在调用接口的时候就可以直接监听数据变化了,等数据成功时直接接收。
最大的区别就是以前的模式下,你需要自己定义一个livedata,等数据返回后在post出来。
现在有了工厂,直接返回livedata类型,不用另外定义,省略了一个流程。
但是不得不说,在先有很多架构上不太符合,但是在某些场景下,可以达到奇效,不止步于retrofit。