首页 > 其他分享 >Retrofit通过OkHttp发送请求的过程

Retrofit通过OkHttp发送请求的过程

时间:2023-06-19 12:03:15浏览次数:27  
标签:args retrofit method 发送 callFactory Call OkHttp call Retrofit


retrofit是对okhttp的封装。retrofit使用注解来创建请求的,retrofit的注解有哪些,分别代表什么意义,有大量的博客都有介绍,我就不重复了。但注解创建的请求,很显然是不能被okhttp识别的,其中必定有个转换的过程。这个过程我没有搜到,所以我阅读了retrofig源码,把用注解创建的请求,变为okhttp请求的过程梳理了出来。这篇博客是分析retrofit如何生成请求,如何转换为okhttp的请求并发送出去的过程。
源码分析基于com.squareup.retrofit2:retrofit:2.9.0

Retrofit的使用实例

Retrofit retrofit = new Retrofit.Builder().baseUrl("<https://api.uomg.com/>")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
ICity iCity = retrofit.create(ICity.class);
Call cityCall = iCity.aa("<https://node.kg.qq.com/play?s=YaCv8EYfJunVWYcH>"); // 这里url是个请求参数
cityCall.enqueue(Callback);


public interface ICity {
    @GET("api/get.kg")
    Call aa(@Query("songurl") String str);
    
    @FormUrlEncoded
    @POST("api/get.kg")
    Call post(@Field("songurl") String str);

}

创建请求类的对象的过程,使用了动态代理

retrofit.create()只是创建ICity的动态代理,只有在执行iCity.aa()时,里面的InvocationHandler.invoke()才会执行。 ICity中,每一个方法都是一个请求,ICity是把这些请求封装到一个类。

  • Retrofit.java
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);
          	}
        });
}

private void validateServiceInterface(Class<?> service) {
 	Platform platform = Platform.get();
 	for (Method method : service.getDeclaredMethods()) {
 		if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
 			loadServiceMethod(method);
 		}
 	}
}

当iCity.aa()执行时,InvocationHandler.invoke()会被执行。别问为啥,动态代理就是这么干的。
invoke()的三个入参中,Object proxy, Method method, @Nullable Object[] args,Object proxy是ICity的代理对象(细说我就不知道了);Method method是java反射中的Method,由于前面执行的是aa()方法,所以这里就是这个方法;Object[] args是aa()方法的入参。
method.getDeclaringClass()返回的是ICity,isDefaultMethod()条件:public、非抽象、非静态,所以会走loadServiceMethod(method).invoke(args)。

loadServiceMethod(method).invoke(args),loadServiceMethod部分

看核心的loadServiceMethod。这段代码是从缓存中获取ServiceMethod,如果获取失败,则自己生成一个,并写入缓存。
值得注意一点是,同步代码内重新从缓存获取了一次。

ServiceMethod<?> loadServiceMethod(Method method) {
 	ServiceMethod<?> result = serviceMethodCache.get(method);
 	if (result != null) return result;
 	synchronized (serviceMethodCache) {
 		result = serviceMethodCache.get(method);
 		if (result == null) {
 			result = ServiceMethod.parseAnnotations(this, method);
 			serviceMethodCache.put(method, result);
 		}
 	}
 	return result;
}

下面来看ServiceMethod的生成过程。

  • ServiceMethod.java
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

核心部分在HttpServiceMethod.parseAnnotations()中

  • HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
        Retrofit retrofit, Method method, RequestFactory requestFactory) {
    /* 对kotlin支持 */
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
    } else {
        adapterType = method.getGenericReturnType();
    }
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
        // 疑问一、要求返回ServiceMethod,为什么返回CallAdapted
        return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); // 注意CallAdapted与前面CallAdapter,消息看错成一个类
    }
}

疑问一、要求返回ServiceMethod,为什么返回CallAdapted

肯定有继承关系。具体是CallAdapted 继承于 HttpServiceMethod,HttpServiceMethod又是ServiceMethod的实现。

class CallAdapted extends HttpServiceMethod {

    private final CallAdapter callAdapter;
    
    CallAdapted(
            RequestFactory requestFactory,
            okhttp3.Call.Factory callFactory,
            Converter responseConverter,
            CallAdapter callAdapter) {
        super(requestFactory, callFactory, responseConverter);
        this.callAdapter = callAdapter;
    }

    HttpServiceMethod {
        private final RequestFactory requestFactory;
        private final okhttp3.Call.Factory callFactory;
        private final Converter responseConverter;
}

loadServiceMethod(method).invoke(args),invoke部分

入口在HttpServiceMethod.invoke

final @Nullable ReturnT invoke(Object[] args) {
    Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}

这里的Call、OkHttpCall都是Retorfit自定义的类,他们在retrofit2包中,不是OkHttp的。OkHttpCall把RequestFactory、入参、callFactory、responseConverter封装起来而已。

先不管这些入参怎么来的。HttpServiceMethod类中,adapt是抽象方法,前面的loadServiceMethod()实际返回的是CallAdapted对象,所以adapt也应该看CallAdapted的。

  • CallAdapted是内部类,HttpServiceMethod$CallAdapted.java
protected ReturnT adapt(Call call, Object[] args) {
    return callAdapter.adapt(call);
}

要找到callAdapter是怎么创建的,找到这个对象的类,才能找到实现。callAdapter对象的创建,由loadServiceMethod()启动,过程在HttpServiceMethod.parseAnnotations()中,

  • HttpServiceMethod.java
static HttpServiceMethod parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    CallAdapter callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); // 不要把CallAdapter和后面的CallAdapted混淆
    if (!isKotlinSuspendFunction) {
        return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }
}

这一句 CallAdapter callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); 依次调用关系:createCallAdapter 》 retrofit.callAdapter 》 nextCallAdapter,最后到

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
        CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
        return adapter;
    }
}

这里callAdapterFactories.get(i)得到的应该是应该是Platform.defaultCallAdapterFactories()内创建的DefaultCallAdapterFactory对象,看起get()方法,这是创建CallAdapter的方法。

  • DefaultCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
        return null;
    }
    return new CallAdapter<Object, Call<?>>() {
        @Override
        public Type responseType() {
            return responseType;
        }
        @Override
        public Call<Object> adapt(Call<Object> call) {
            return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
        }
    };
}

所以上面的callAdapter,是在这儿创建的new CallAdapter()。 前面还提到的的callAdapter.adapt(),应该走到这儿来;由CallAdapter生产的Call,就是这里的ExecutorCallbackCall。判断条件分析略过。 可以看到ExecutorCallbackCall跟Call是存在继承关系的。 注意new ExecutorCallbackCall<>(executor, call)有一个入参call,ExecutorCallbackCall里面其实还是调用这个入参完成的,ExecutorCallbackCall只是充当一个代理,或者适配器的作用,真正还是参数call,即OkHttpCall。

  • ExecutorCallbackCall是内部类,详见DefaultCallAdapterFactory$ExecutorCallbackCall.java
class ExecutorCallbackCall<T> implements Call<T> {
    final Call<T> delegate;
    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { // 入参call
        this.delegate = delegate;
    }
    @Override
    public void enqueue(final Callback<T> callback) {
        delegate.enqueue(Callback);
    }
}

请求的发送流程。Call.enqueue()

前面Retrofit使用实例中,cityCall.enqueue,是Retrofit中的Call入列。这个Call是OkHttpCall,我们要看Retrofit的Call怎么转换为OkHttp的Call,并如入列。

  • OkHttpCall.java
public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    call = rawCall = createRawCall();
    call.enqueue();
}

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    return call;
}

是通过这一句callFactory.newCall(requestFactory.create(args))创建的OkHttp的Call,有三个问题,1、callFactory(是okhttp3.Call.Factory)怎么创建的;2、RequestFactory的创建过程;3、RequestFactory创建OkHttp的Request的过程。

1、callFactory(是okhttp3.Call.Factory)怎么创建的

callFactory是okhttp中的类,用来生成okhttp中的call。下面是callFactory在retrofit各环节中的使用流程。
1、Retrofit是用建造者模式创建的,callFactory在这里被创建,并保存为Retrofit类的成员变量。

  • Retrofit.java
build() {
    callFactory = new OkHttpClient();
    new Retrofit(callFactory...);
}

2、在HttpServiceMethod.parseAnnotations()中,从retrofit中获取callFactory,并通过构造方法传递给CallAdapted。

  • HttpServiceMethod.java
static ... parseAnnotations() {
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    new CallAdapted<>(callFactory...);
}

3、callFactory传递给CallAdapted后,在经由父类传递给retrofit2.Call。

  • HttpServiceMethod.java
private final okhttp3.Call.Factory callFactory;
    HttpServiceMethod(...okhttp3.Call.Factory callFactory) {
    this.callFactory = callFactory;
}

/* callFactory是HttpServiceMethod的成员变量,由外部通过构造方法传入 */
final @Nullable ReturnT invoke(Object[] args) {
    Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}

4、callFactory创建okhttp中的Call的过程。
承上,callFactory被封装到retrofit2.Call之后,HttpServiceMethod类调用adapter()方法,实际走到CallAdapted.adapter();

protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
    return callAdapter.adapt(call);
}

前面介绍过,callAdapter.adapt()生成的Call只是代理(适配器),真正的Call对象还是这个入参Call。他的真实面目在HttpServiceMethod.invoke()中

final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
}

这里就把callFactory传递给了Call对象。在Call.enqueue()中,用这个callFactory生成okhttp的Call。

  • OkHttpCall.java
enquque() {
    createRawCall();
}

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
}

2、RequestFactory的创建过程;

Retrofit.loadServiceMethod()调用ServiceMethod.parseAnnotations();

  • ServiceMethod.java
static ... parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
}

深入RequestFactory.parseAnnotation,看RequestFactory的创建过程,及重要的参数。

  • RequestFactory.java
RequestFactory {
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new Builder(retrofit, method).build();
    }

    RequestFactory(Builder builder) {
        method = builder.method; // java.lang.reflect.Method,对应一个请求接口,对应一个RequestFactory
        baseUrl = builder.retrofit.baseUrl; // 域名
        httpMethod = builder.httpMethod; // get、post
        relativeUrl = builder.relativeUrl; // 网关,解析接口的注解得到,详见RequestFactory.parseHttpMethodAndPath(),根源于method.getAnnotations()
        parameterHandlers = builder.parameterHandlers; // 请求参数
...
}

    /** 建造者是内部类 */
    class Builder {
 	Annotation[] methodAnnotations;
        Annotation[][] parameterAnnotationsArray;
        Builder() {
            this.methodAnnotations = method.getAnnotations(); // 获取接口方法的注解
            this.parameterTypes = method.getGenericParameterTypes();
            this.parameterAnnotationsArray = method.getParameterAnnotations(); // 获取接口方法的形式参数的注解
}

        RequestFactory build() {
            for (Annotation annotation : methodAnnotations) {
                /* 获取get|post请求方式,获取网关 */
                parseMethodAnnotation(annotation);
            }

            int parameterCount = parameterAnnotationsArray.length;
            parameterHandlers = new ParameterHandler<?>[parameterCount];
            for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
                /* 解析接口方法的参数注解,是请求参数的key、value对应 */
                parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
            }
            return new RequestFactory(this);
        }
    }
}

3、RequestFactory创建OkHttp的Request的过程

OkHttpCall.createRawCall()中,okhttp3.Call call = callFactory.newCall(requestFactory.create(args)),callFactory.newCall的入参是okhttp中的Request,requestFactory.create就是返回一个okhttp3.Request。

  • RequestFactory.java
ParameterHandler[] handlers = (ParameterHandler[]) parameterHandlers;
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl...);
    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}

其中requestBuilder.get()是将请求的进一步处理,如全url,符合RequestBody格式的参数等。build()创建Request对象。


标签:args,retrofit,method,发送,callFactory,Call,OkHttp,call,Retrofit
From: https://blog.51cto.com/u_16163442/6512314

相关文章

  • 使用Kotlin+Rretrofit+rxjava+设计模式+MVP封装网络请求
    0、前言:kotlin使用起来非常顺畅,尤其是结合rxjava、设计模式使用,你会发现写代码原来可以这么开心!什么?你还在使用java?赶紧去学一下kotlin吧!我相信你一定会对他爱不释手,kotlin也很容易学,花一天的时间就可以从java切换为kotlin一、正文本文主要介绍如何使用kotlin封装网络请求的工具,结......
  • 使用go标准库发送邮件
    在学习go语言的过程中,发现smtp邮件服务也在go语言的标准库中支持,所以写了一个小demo测试是否能够真的发送邮件,代码如下:packagemainimport( "crypto/tls" "log" "net/smtp")const( smtpServer="smtp.163.com"//smtp服务器地址 account="xxxxxxxxxx"......
  • axios headers设置发送接收文件类型
    接受blobexportfunctionexportTeacherActivitiesState(data:any):Promise<IResponse<Blob>>{ returnrequest({  baseURL,  url:"/Api/CourseEvaluationManage/ActivityStatistics/ExportTeacherActivitiesState",  method:"post......
  • 【Python入门教程】调取电脑摄像头并发送照片至邮箱
    ​        本博文纯属娱乐,仅供大家学习参考,不得以此侵犯他人隐私。本篇文章参考Python研究者的python窃取摄像头的图片。在这里先感谢大佬的付出,大家可以去关注一下。一、获取邮箱授权码        授权码用于调用邮箱实现邮件的发送,这里以QQ邮箱做演示,在设置的账......
  • 观察者模式:发送状态变化通知
    观察者模式是一种行为设计模式,它允许对象在发生特定事件时通知其他对象。这些被通知的对象被称为观察者,而通知它们的对象称为主题或可观察对象。该模式使用了松散耦合的原则,因此主题和观察者之间没有太多的依赖关系。示例代码:importjava.util.ArrayList;importjava.util.Li......
  • TBrookHTTPServer
    TBrookHTTPServerbrook是c写的,以dll供delphi和lazarus调用。unitBrook.HTTPServer;//cxg2023-2-12interfaceusessock.router,System.JSON,Json.help,keyValue.serialize,api.router,BrookHTTPRequest,BrookHTTPResponse,BrookHTTPServer,global,IniFiles,......
  • 医院设置——锁定和发送签名
    //8、医院设置锁定和解锁:锁定时则无法对医院设置进行操作,只有status=1解锁了才能操作@PutMapping("lockHospitalSet/{id}/{status}")publicResultlockHospitalSet(@PathVariablelongid,@PathVariableIntegerstatus){//根据id查询医院设置信息......
  • 详解MySQL Server端如何发送结果集给客户端
    MySQLServer和Client之间的交互有一套定义得很明确的协议,称为MySQLClient/ServerProtocol。写数据库的人,只需要遵循这套协议来写程序,就能让自己的数据库被各种MySQL客户端连接,如mysql命令行,phpmysql,JDBC等等。这是一个非常诱人的设计选择(DesignChoice)!如果自己实现一套协议,写......
  • python发送文件
      发送端importsocketimportosimporttimefilename=input('pleaseenterthefilenameyouwanttosend:\n')filesize=str(os.path.getsize(filename))fname1,fname2=os.path.split(filename)client_addr=('127.0.0.1',9999)f=op......
  • 直播app源码技术之直播间内消息发送与接收的实现
     在日常生活中,很多人有看直播的习惯,直播也给这些人带来了欢乐,不仅仅是因为直播间里的主播的直播内容的精彩,还有就是他们可以在直播间里进行互动,当然,互动的方式有很多,像是送礼物、点赞、发红包等等,今天我要说的互动方式和这些都不同,但是确是用户观看直播app源码平台直播的主要互......