Retrofit 支持suspend函数源码分析

Retrofit 2.6.0 之后支持接口suspend函数配合协程使用,举个例子:

ApiService

1interface LoginApiService : BaseService { 2 @GET("/wxarticle/chapters/json") 3 suspend fun getChapters(): BaseResponse<List<Chapters>> 4}

Repository

1class LoginRepository : BaseRepository() { 2 suspend fun getChapters(): BaseResponse<List<Chapters>> { 3 val service = retrofit.create(LoginApiService::class.java) 4 return withContext(Dispatchers.IO) { 5 service.getChapters() 6 } 7 } 8} 9

从API调用入手Retrofit.create()

1 2 public <T> T create(final Class<T> service) { 3 validateServiceInterface(service); 4 return (T) 5 Proxy.newProxyInstance( 6 service.getClassLoader(), 7 new Class<?>[] {service}, 8 new InvocationHandler() { 9 private final Platform platform = Platform.get(); 10 private final Object[] emptyArgs = new Object[0]; 11 12 @Override 13 public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) 14 throws Throwable { 15 // If the method is a method from Object then defer to normal invocation. 16 if (method.getDeclaringClass() == Object.class) { 17 return method.invoke(this, args); 18 } 19 args = args != null ? args : emptyArgs; 20 return platform.isDefaultMethod(method) 21 ? platform.invokeDefaultMethod(method, service, proxy, args) 22 : loadServiceMethod(method).invoke(args); 23 } 24 }); 25 } 26

动态代理,拦截接口方法,实际调用Retrofit.loadServiceMethod()

1 2 ServiceMethod<?> loadServiceMethod(Method method) { 3 ServiceMethod<?> result = serviceMethodCache.get(method); 4 if (result != null) return result; 5 6 synchronized (serviceMethodCache) { 7 result = serviceMethodCache.get(method); 8 if (result == null) { 9 result = ServiceMethod.parseAnnotations(this, method); 10 serviceMethodCache.put(method, result); 11 } 12 } 13 return result; 14 }

ServiceMethod.parseAnnotations()

1static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { 2 RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); 3 4 Type returnType = method.getGenericReturnType(); 5 if (Utils.hasUnresolvableType(returnType)) { 6 throw methodError( 7 method, 8 "Method return type must not include a type variable or wildcard: %s", 9 returnType); 10 } 11 if (returnType == void.class) { 12 throw methodError(method, "Service methods cannot return void."); 13 } 14 15 return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); 16 }

RequestFactory.parseAnnotations()

1 static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { 2 return new Builder(retrofit, method).build(); 3 } 4new Builder(retrofit, method).build() 5 6RequestFactory build() { 7 for (Annotation annotation : methodAnnotations) { 8 parseMethodAnnotation(annotation); 9 } 10 ...... 11 int parameterCount = parameterAnnotationsArray.length; 12 parameterHandlers = new ParameterHandler<?>[parameterCount]; 13 for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { 14 parameterHandlers[p] = 15 parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); 16 } 17 ...... 18 return new RequestFactory(this); 19 }

build()中遍历解析注解,parseParameter()中判断是否为挂起函数。

RequestFactory.parseParameter()

1 ...... 2if (Utils.getRawType(parameterType) == Continuation.class) { 3 isKotlinSuspendFunction = true; 4 return null; 5 } 6 ......

回看ServiceMethod.parseAnnotations()返回值HttpServiceMethod.parseAnnotations()

1 boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; 2 boolean continuationWantsResponse = false; 3 4 if (isKotlinSuspendFunction) { 5 if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { 6 responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType); 7 continuationWantsResponse = true; 8 } 9 } 10 11 if (!isKotlinSuspendFunction) { 12 return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); 13 } else if (continuationWantsResponse) { 14 //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. 15 return (HttpServiceMethod<ResponseT, ReturnT>) 16 new SuspendForResponse<>( 17 requestFactory, 18 callFactory, 19 responseConverter, 20 (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); 21 } else { 22 //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. 23 return (HttpServiceMethod<ResponseT, ReturnT>) 24 new SuspendForBody<>( 25 requestFactory, 26 callFactory, 27 responseConverter, 28 (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, 29 continuationBodyNullable); 30 }

这里走else if

1return (HttpServiceMethod<ResponseT, ReturnT>) 2 new SuspendForResponse<>( 3 requestFactory, 4 callFactory, 5 responseConverter, 6 (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter)

retrofit.loadServiceMethod()最终返回SuspendForResponse

1 static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> { 2 private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter; 3 private final boolean isNullable; 4 5 SuspendForBody( 6 RequestFactory requestFactory, 7 okhttp3.Call.Factory callFactory, 8 Converter<ResponseBody, ResponseT> responseConverter, 9 CallAdapter<ResponseT, Call<ResponseT>> callAdapter, 10 boolean isNullable) { 11 super(requestFactory, callFactory, responseConverter); 12 this.callAdapter = callAdapter; 13 this.isNullable = isNullable; 14 } 15 16 @Override 17 protected Object adapt(Call<ResponseT> call, Object[] args) { 18 call = callAdapter.adapt(call); 19 Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1]; 20 try { 21 return isNullable 22 ? KotlinExtensions.awaitNullable(call, continuation) 23 : KotlinExtensions.await(call, continuation); 24 } catch (Exception e) { 25 return KotlinExtensions.suspendAndThrow(e, continuation); 26 } 27 } 28 }

回看loadServiceMethod().invoke()->ServiceMethod抽象方法

1abstract @Nullable T invoke(Object[] args)

子类HttpServiceMethod.invoke()

1 @Override 2 final @Nullable ReturnT invoke(Object[] args) { 3 Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); 4 return adapt(call, args); 5 } 6 7 protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

调用到抽象方法adapt(),loadServiceMethod()方法返回值SuspendForResponse继承HttpServiceMethod重写了该方法

1 @Override 2 protected Object adapt(Call<ResponseT> call, Object[] args) { 3 call = callAdapter.adapt(call); 4 5 //noinspection unchecked Checked by reflection inside RequestFactory. 6 Continuation<Response<ResponseT>> continuation = 7 (Continuation<Response<ResponseT>>) args[args.length - 1]; 8 9 // See SuspendForBody for explanation about this try/catch. 10 try { 11 return KotlinExtensions.awaitResponse(call, continuation); 12 } catch (Exception e) { 13 return KotlinExtensions.suspendAndThrow(e, continuation); 14 } 15 }

KotlinExtensions.awaitResponse()

1suspend fun <T> Call<T>.awaitResponse(): Response<T> { 2 return suspendCancellableCoroutine { continuation -> 3 continuation.invokeOnCancellation { 4 cancel() 5 } 6 enqueue(object : Callback<T> { 7 override fun onResponse(call: Call<T>, response: Response<T>) { 8 continuation.resume(response) 9 } 10 11 override fun onFailure(call: Call<T>, t: Throwable) { 12 continuation.resumeWithException(t) 13 } 14 }) 15 } 16}

call.enqueue()内部调用continuation.resume(response)将返回值传给挂起函数,流程结束。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )