Android

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。

上一篇 下一篇

猜你喜欢

热点阅读