Retrofit如何封装同步的CallAdapter

904 阅读1分钟

需求背景

应用需要在后台请求一系列的后端接口,采用RxJava或者LiveData形式会存在一系列的observeXXX,如:

interface ITestService {
    fun getTest1(): LiveData<BaseResponse<String>>
    fun getTest2(): LiveData<BaseResponse<String>>
    fun getTest3(): LiveData<BaseResponse<String>>
}

代码调用如下

retrofit.getTest1().observe(activity) {
    retrofit.getTest2().observe(activity) {
        retrofit.getTest2().observe(activity) {
            // do something
        }
    }
}

可以看出,采用RxJava/LiveData会导致嵌套问题

解决方案1 -- 使用Kotlin协程

利用协程将callback化为直接调用,此方案不在此文讨论范围内

解决方案2 -- 使用同步CallAdapter

2.1 自定义CallAdapter

class MyCallAdapter<R>(private var responseType: Type) :
    CallAdapter<R, R> {
    override fun responseType(): Type {
        return responseType
    }

    override fun adapt(call: Call<R>): R? {
        return try {
            val result = call.execute()
            result.body()
        } catch (e: Exception) {
            null
        }
    }
}

2.2 自定义CallAdapter.Factory

class MyCallAdapterFactory : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<out Annotation>,
        retrofit: Retrofit
    ): CallAdapter<*, *> {
        return MyCallAdapter<Any>(returnType)
    }
}

2.3 初始化Retrofit并使用

    interface ITestService {
        fun getTest1():BaseResponse<String>
    }
val testservice = Retrofit.Builder().client(client())
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .addCallAdapterFactory(MyCallAdapterFactory())
    .build()
    .create(ITestService::class.java)

注:若返回结果对BaseResponse包裹了一层,如:

sealed class HttpResult<out T> {
    class HttpResultSuccess<T: Any>(val value:T) : HttpResult<T>()
    class HttpResultError(val error:Throwable) : HttpResult<Nothing>()
}
    interface ITestService {
        fun getTest1():HttpResult<BaseResponse<String>>
    }

稍加改造即可

class MyCallAdapterFactory : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<out Annotation>,
        retrofit: Retrofit
    ): CallAdapter<*, *> {
        val realReturnType = getParameterUpperBound(0, returnType as ParameterizedType)
        return MyCallAdapter<Any>(realReturnType)
    }
}
class MyCallAdapter<R>(private var responseType: Type) :
    CallAdapter<R, HttpResult<R>> {
    override fun responseType(): Type {
        return responseType
    }

    override fun adapt(call: Call<R>): HttpResult<R> {
        try {
            val result = call.execute()
            if (result.isSuccessful) {
                val body = result.body()
                if (body != null) {
                    return HttpResult.HttpResultSuccess(body)
                }
            }
            // 自行处理异常
            return HttpResult.HttpResultError(Exception())
        } catch (e: Exception) {
            return HttpResult.HttpResultError(e)
        }
    }
}