共计 8341 个字符,预计需要花费 21 分钟才能阅读完成。
1. 简介
Retrofit 对协程的反对十分的简陋。在 kotlin 中应用不合乎 kotlin 的优雅
interface TestServer {@GET("banner/json")
suspend fun banner(): ApiResponse<List<Banner>>}
// 实现并行捕捉异样的网络申请
fun oldBanner(){
viewModelScope.launch {
// 传统模式应用 retrofit 须要 try catch
val bannerAsync1 = async {
var result : ApiResponse<List<Banner>>? = null
kotlin.runCatching {service.banner()
}.onFailure {Log.e("banner",it.toString())
}.onSuccess {result = it}
result
}
val bannerAsync2 = async {
var result : ApiResponse<List<Banner>>? = null
kotlin.runCatching {service.banner()
}.onFailure {Log.e("banner",it.toString())
}.onSuccess {result = it}
result
}
bannerAsync1.await()
bannerAsync2.await()}
}
一层嵌套一层,属实无法忍受。kotlin 应该一行代码解决问题,才合乎 kotlin 的优雅
应用本框架后
interface TestServer {@GET("banner/json")
suspend fun awaitBanner(): Await<List<Banner>>}
// 实现并行捕捉异样的网络申请
fun parallel(){
viewModelScope.launch {val awaitBanner1 = service.awaitBanner().tryAsync(this)
val awaitBanner2 = service.awaitBanner().tryAsync(this)
// 两个接口一起调用
awaitBanner1.await()
awaitBanner2.await()}
}
3. 查看 Retrofit 源码
先看 Retrofit create 办法
public <T> T create(final Class<T> service) {validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);// 具体调用
}
});
}
loadServiceMethod(method).invoke(args) 进入这个办法看具体调用
咱们查看 suspenForResponse 中的 adapt
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {call = callAdapter.adapt(call);// 如果用户不设置 callAdapterFactory 就应用 DefaultCallAdapterFactory
//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
// See SuspendForBody for explanation about this try/catch.
try {return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
前面间接交给协程去调用 call。具体的 okhttp 调用在 DefaultCallAdapterFactory。或者用户自定义的 callAdapterFactory 中
因而咱们这边能够自定义 CallAdapterFactory 在调用后不进行网络申请的拜访,在用户调用具体方法时候再进行网络申请拜访。
4. 自定义 CallAdapterFactory
Retrofit 在调用后间接进行了网络申请,因而很不好操作。咱们把网络申请的控制权放在咱们手里,就能随便操作。
class ApiResultCallAdapterFactory : CallAdapter.Factory() {override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
// 查看 returnType 是否是 Call<T> 类型的
if (getRawType(returnType) != Call::class.java) return null
check(returnType is ParameterizedType) {"$returnType must be parameterized. Raw types are not supported"}
// 取出 Call<T> 里的 T,查看是否是 Await<T>
val apiResultType = getParameterUpperBound(0, returnType)
// 如果不是 Await 则不禁本 CallAdapter.Factory 解决 兼容失常模式
if (getRawType(apiResultType) != Await::class.java) return null
check(apiResultType is ParameterizedType) {"$apiResultType must be parameterized. Raw types are not supported"}
// 取出 Await<T> 中的 T 也就是 API 返回数据对应的数据类型
// val dataType = getParameterUpperBound(0, apiResultType)
return ApiResultCallAdapter<Any>(apiResultType)
}
}
class ApiResultCallAdapter<T>(private val type: Type) : CallAdapter<T, Call<Await<T>>> {override fun responseType(): Type = type
override fun adapt(call: Call<T>): Call<Await<T>> {return ApiResultCall(call)
}
}
class ApiResultCall<T>(private val delegate: Call<T>) : Call<Await<T>> {
/**
* 该办法会被 Retrofit 解决 suspend 办法的代码调用,并传进来一个 callback, 如果你回调了 callback.onResponse,那么 suspend 办法就会胜利返回
* 如果你回调了 callback.onFailure 那么 suspend 办法就会抛异样
*
* 所以咱们这里的实现是回调 callback.onResponse, 将 okhttp 的 call delegate
*/
override fun enqueue(callback: Callback<Await<T>>) {
// 将 okhttp call 放入 AwaitImpl 间接返回,不做网络申请。在调用 AwaitImpl 的 await 时才真正开始网络申请
callback.onResponse(this@ApiResultCall, Response.success(delegate.toResponse()))
}
}
internal class AwaitImpl<T>(private val call : Call<T>,) : Await<T> {override suspend fun await(): T {
return try {call.await()
} catch (t: Throwable) {throw t}
}
}
通过下面自定义 callAdapter 后,咱们提早了网络申请,在调用 Retrofit 后并不会申请网络,只会将网络申请所须要的 call 的放入 await 中。
@GET("banner/json")
suspend fun awaitBanner(): Await<List<Banner>>
咱们拿到的 Await<List> 并没有做网络申请。在这个实体类中蕴含了 okHttp 的 call。
这时候咱们能够定义如下办法就能捕捉异样
suspend fun <T> Await<T>.tryAsync(
scope: CoroutineScope,
onCatch: ((Throwable) -> Unit)? = null,
context: CoroutineContext = SupervisorJob(scope.coroutineContext[Job]),
start: CoroutineStart = CoroutineStart.DEFAULT
): Deferred<T?> = scope.async(context, start) {
try {await()
} catch (e: Throwable) {onCatch?.invoke(e)
null
}
}
同样并行捕捉异样的申请,就能够通过如下形式调用,优雅简洁了很多
/**
* 并行 async
*/
fun parallel(){
viewModelScope.launch {val awaitBanner1 = service.awaitBanner().tryAsync(this)
val awaitBanner2 = service.awaitBanner().tryAsync(this)
// 两个接口一起调用
awaitBanner1.await()
awaitBanner2.await()}
}
这时候咱们发现网络申请胜利了,解析数据失败。因为咱们在数据里面套了一层 await。必定无奈解析胜利。
本着哪里谬误解决哪里的思路,咱们自定义 Gson 解析
5. 自定义 Gson 解析
class GsonConverterFactory private constructor(private var responseCz : Class<*>,var responseConverter : GsonResponseBodyConverter, private val gson: Gson) : Converter.Factory() {
override fun responseBodyConverter(
type: Type, annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *> {
var adapter : TypeAdapter<*>? = null
// 查看是否是 Await<T>
if (Utils.getRawType(type) == Await::class.java && type is ParameterizedType){
// 取出 Await<T> 中的 T
val awaitType = Utils.getParameterUpperBound(0, type)
if(awaitType != null){adapter = gson.getAdapter(TypeToken.get(ParameterizedTypeImpl[responseCz,awaitType]))
}
}
// 不是 awiat 失常解析,兼容失常模式
if(adapter == null){adapter= gson.getAdapter(TypeToken.get(ParameterizedTypeImpl[responseCz,type]))
}
return responseConverter.init(gson, adapter!!)
}
}
class MyGsonResponseBodyConverter : GsonResponseBodyConverter() {override fun convert(value: ResponseBody): Any {val jsonReader = gson.newJsonReader(value.charStream())
val data = adapter.read(jsonReader) as ApiResponse<*>
val t = data.data
val listData = t as? ApiPagerResponse<*>
if (listData != null) {
// 如果返回值值列表封装类,且是第一页并且空数据 那么给空异样 让界面显示空
if (listData.isRefresh() && listData.isEmpty()) {throw ParseException(NetConstant.EMPTY_CODE, data.errorMsg)
}
}
// errCode 不等于 SUCCESS_CODE,抛出异样
if (data.errorCode != NetConstant.SUCCESS_CODE) {throw ParseException(data.errorCode, data.errorMsg)
}
return t!!
}
}
6. 本框架应用
增加依赖
implementation "io.github.cnoke.ktnet:api:?"
写一个网络申请数据基类
open class ApiResponse<T>(
var data: T? = null,
var errorCode: String = "",
var errorMsg: String = ""
)
实现 com.cnoke.net.factory.GsonResponseBodyConverter
class MyGsonResponseBodyConverter : GsonResponseBodyConverter() {override fun convert(value: ResponseBody): Any {val jsonReader = gson.newJsonReader(value.charStream())
val data = adapter.read(jsonReader) as ApiResponse<*>
val t = data.data
val listData = t as? ApiPagerResponse<*>
if (listData != null) {
// 如果返回值值列表封装类,且是第一页并且空数据 那么给空异样 让界面显示空
if (listData.isRefresh() && listData.isEmpty()) {throw ParseException(NetConstant.EMPTY_CODE, data.errorMsg)
}
}
// errCode 不等于 SUCCESS_CODE,抛出异样
if (data.errorCode != NetConstant.SUCCESS_CODE) {throw ParseException(data.errorCode, data.errorMsg)
}
return t!!
}
}
进行网络申请
interface TestServer {@GET("banner/json")
suspend fun awaitBanner(): Await<List<Banner>>}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HeadInterceptor())
.addInterceptor(LogInterceptor())
.build()
val retrofit = Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://www.wanandroid.com/")
.addCallAdapterFactory(ApiResultCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(ApiResponse::class.java,MyGsonResponseBodyConverter()))
.build()
val service: TestServer = retrofit.create(TestServer::class.java)
lifecycleScope.launch {val banner = service.awaitBanner().await()}
异步申请同步申请,异样捕捉参考如下 try 结尾的会捕捉异样,非 try 结尾不会捕捉。
fun banner(){
lifecycleScope.launch {
// 独自解决异样 tryAwait 会解决异样,如果异样返回空
val awaitBanner = service.awaitBanner().tryAwait()
awaitBanner?.let {for(banner in it){Log.e("awaitBanner",banner.title)
}
}
/**
* 不解决异样 异样会间接抛出,对立解决
*/
val awaitBannerError = service.awaitBanner().await()
}
}
/**
* 串行 await
*/
fun serial(){
lifecycleScope.launch {
// 先调用第一个接口 await
val awaitBanner1 = service.awaitBanner().await()
// 第一个接口实现后调用第二个接口
val awaitBanner2 = service.awaitBanner().await()
}
}
/**
* 并行 async
*/
fun parallel(){
lifecycleScope.launch {val awaitBanner1 = service.awaitBanner().async(this)
val awaitBanner2 = service.awaitBanner().async(this)
// 两个接口一起调用
awaitBanner1.await()
awaitBanner2.await()}
}
相干教程
Android 根底系列教程:
Android 根底课程 U - 小结_哔哩哔哩_bilibili
Android 根底课程 UI- 布局_哔哩哔哩_bilibili
Android 根底课程 UI- 控件_哔哩哔哩_bilibili
Android 根底课程 UI- 动画_哔哩哔哩_bilibili
Android 根底课程 -activity 的应用_哔哩哔哩_bilibili
Android 根底课程 -Fragment 应用办法_哔哩哔哩_bilibili
Android 根底课程 - 热修复 / 热更新技术原理_哔哩哔哩_bilibili
本文转自 https://juejin.cn/post/7051437444062773285,如有侵权,请分割删除。