以下内容是以kotlin协程作为retrofit返回挂起函数解析,其实都一样,rxjava或者livedata,因为现在这两个在协程面前都是小弟,所以就不理他们了。
第一步:要分析咱们就透彻点,很显然我们第一步是写接口。
interface IMApi {
/**
* 获取token
*/
@POST("/{org_name}/{app_name}/token")
suspend fun requestToken(@Path("org_name") org_name: String,
@Path("app_name") app_name: String,
@Body body: Map<String, String>): BaseBean<TokenBean>
}
第二步:创建并获取IMApi接口的实现;
object RetrofitManager {
fun imApiImpl() = Retrofit.Builder()
.baseUrl("http://a1.easemob.com/")
.addCallAdapterFactory(BaseAdapterFactory(MainThreadExecutor()))
.addConverterFactory(BaseConverterFactory.create())
.client(initClient())
.build().create(IMApi::class.java)
/*
OKHttp创建
*/
private fun initClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
}
internal class MainThreadExecutor : Executor {
private val handler = Handler(Looper.getMainLooper())
override fun execute(r: Runnable) {
handler.post(r)
}
}
}
第三步:发起请求;
RetrofitManager.imApiImpl().requestToken(org_name,app_name,body)
接下来正式解析:
- 尽然要解析,首先我们就要知道内部是怎么获取到实例,并且触发我们请求的方法的。所以我们从create()方法开始看:可以看到以动态代理的凡是拦截解析方法,最终调用以下关键方法:
ServiceMethod<?> loadServiceMethod(Method method) {
//方法重点
result = ServiceMethod.parseAnnotations(this, method);
return result;
}
//以上方法最终触发以下方法:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//对于我们这个分析,此处isKotlinSuspendFunction应该是true(因为我们请求方法是supend修饰的。具体什么时候确定这个值的啦?下面会单独分析)
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
//获取到Type类型,这个类型就是我们IMApi接口的方法返回类型。
//(但是注意使用协程的时候,获取的到时Call<接口返回类型>,是Call包裹过的,下面的特殊处理可以看出来)
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType = Utils.getParameterLowerBound(0,
(ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
//获取CallAdapter对象,此处获取的就是我们创建retrofit的时候add进去的BaseAdapterFactory(注意,深入方法会发现,我们只会获取list的第一个CallAdapter处理我们请求数据)
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
//同理获取Converter对象,此处获取的就是我们创建retrofit的时候add进去的BaseConverterFactory(注意,深入方法会发现,我们只会获取list的第一个
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//获取我们添加的okhttp实例对象的Factory
okhttp3.Call.Factory callFactory = retrofit.callFactory;
//判断是否是kotlin协程方式,并且返回相应的对象。
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;分析:
一直跟下去我们会发现他是在RequestFactory-->parseParameter(),然而触发这个方法的地方就是在我们上面见到的ServiceMethod-->parseAnnotations(),所以我们可以得到结论:当动态代理获取解析方法和方法参数的时候就已经确定了这个值。
continuationWantsResponse = false解析:
根据此方法里面的判断getRawType()是获取我们接口里面方法的返回类型最外层,很明显不满足条件所以为false。
综合上面分析,我们处理请求解析应该通过SuspendForBody()对象里面处理。根据上面分析我们已经知道最终处理我们返回结果数据是在SuspendForBody类里面做的处理,那现在关心的是怎么触发我们自定义的adapter和conver的啦?
- 首选看我们自定义的adapter:
进入SuspendForBody类,我们能找到以下函数,会发现这个函数是否和我们自定义里面的adapter里面函数相识的样子,而且这个类是把我们自定义的adapter带进来,还进行了触发,最终通过携程返回一个挂起函数,这里正好和我们定义接口里面的函数是挂起函数相呼应。这个时候我们就能理解为什么我们写接口里面函数的时候要写挂起函数了吧。
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
try {
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
接下来我们看Conver在哪里触发,我们发现上面类是传入了我们的conver的,但是里面却没有使用它,而且传给了父类,所以我们找找父类,发现在invoke方法里面把我们的conver传递给了OKHttpCall,然而这个invoke又是在我们动态代理加载函数的时候触发的,所以大致推测,肯定是在这个类里面触发我们conver()方法的 ,接着我们跟进去,找到了以下函数:
我们能很清晰的看到,如果接口情况成功code满足添加的话就会触发我们的conver,这个时候我们就知道我们自定义解析返回值触发是在okhttpcall里面解析返回值的时候了,从这里我们还能看出,如果请求结果(code < 200 || code >= 300 || code == 204 || code == 205)都不会触发我们的自定义conver。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
总结:所以根据上面分析,我们大致罗列一下整个流程:(流程图太麻烦,就不画了)
create-->loadServiceMethod--> ServiceMethod.parseAnnotations--> ServiceMethod.parseAnnotations[整个函数做了几件事:createCallAdapter,createResponseConverter, new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);]-->SuspendForBody-->adapt-->invoke-->OkHttpCall-->parseResponse.
整个触发流程大致如此,到这里我们retrofit解析respose就结束了。