Retrofit 源码解析

Retrofit 是什么?

Retrofit负责将HttpAPI接口转换为Java接口,它是一个RESTful风格的Http网络请求框架的封装,实际上的网络请求还是由OkHttp完成,Retrofit仅负责网络接口请求的封装。

  • App 中通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数、HeaderUrl 等信息,之后由 OkHttp 完成后续的请求操作。
  • 在服务端返回数据之后,OkHttp 将原始的结果交给 RetrofitRetrofit根据用户的需求对结果进行解析。

所以,网络请求的本质仍旧是 OkHttp 完成的,Retrofit 只是帮使用者来进行工作简化的,比如配置网络,处理数据等工作,提高这一系列操作的复用性。

如何使用 Retrofit

  1. 引入依赖
arduino 复制代码
implementation 'com.squareup.okhttp3:okhttp:+'// OkHttp网络库
implementation 'com.squareup.retrofit2:retrofit:+'// retrofit库
implementation 'com.google.code.gson:gson:+'// gson生成和解析库
implementation 'com.squareup.retrofit2:converter-gson:+'//Gson转换器,请求结果转换为数据Model
implementation 'com.squareup.retrofit2:adapter-rxjava2:+'// 配合Rxjava使用
implementation 'io.reactivex.rxjava3:rxjava:+'//
implementation 'io.reactivex.rxjava3:rxandroid:+'//一个帮助做异步请求的框架,类似于AsyncTask
  1. 创建 API 请求接口

RetrofitHttp 请求抽象成 Java 接口:采用注解描述网络请求参数和配置网络请求参数

less 复制代码
//自己定义的 API 请求接口
interface MyApiService {
    @GET("{source}/media/list")
    fun getMediaList(@Path("source") source: String): Call<List<Media>>
}
  1. 创建 Retorfit 实例,并发送请求
kotlin 复制代码
//1. 创建Retrofit实例
val retrofit = Retrofit.Builder()
    .baseUrl("http://api.test.com/") //设置网络请求的Url地址
    .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器
    .build()

//2. 创建MyApiService对象
val apiService = retrofit.create(MyApiService::class.java)

//3. 获取Call对象
val mediaList = apiService.getMediaList("qqm")

//4. 调用call.enqueue发起异步请求
mediaList.enqueue(object: Callback<List<Media>> {
    override fun onResponse(call: Call<List<Media>>, response: Response<List<Media>>) {
        Log.d(TAG, "mediaList response code is ${response.code()}")
    }

    override fun onFailure(call: Call<List<Media>>, t: Throwable) {
        Log.e(TAG, "mediaList failure")
    }

})

Retrofit 的核心概念

注解

Retrofit 使用注解来描述 HTTP 请求的参数、URL 和请求方法。以下是常见的注解:

  • @GET:发送 GET 请求
  • @POST:发送 POST 请求
  • @Path:替换 URL 中的参数
  • @Query:添加查询参数
  • @Body:发送请求体
less 复制代码
public interface ApiService {
 @GET("posts/{id}")
 Call<Post> getPostById(@Path("id") int postId);
}

CallAdapter

  • CallAdapter 主要用于将 RetrofitCall 类型适配到其他类型,例如 LiveDataRxJavaObservable 等,Retrofit 内置了常见的 CallAdapter,如 RxJavaCallAdapterLiveDataCallAdapter
  • 使用场景: 当你希望在进行网络请求时,直接得到一个特定类型的响应,而不是 Retrofit 默认的 Call 类型时,就可以使用 CallAdapter
scss 复制代码
Retrofit retrofit = new Retrofit.Builder()
 .baseUrl(BASE_URL)
 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
 .build();

Converter

  • 作用: Converter 主要用于将请求体和响应体进行转换,将网络请求的原始数据转换为你需要的实体对象,以及将实体对象转换为网络请求的原始数据。
  • 使用场景: 当你需要自定义请求体和响应体的转换逻辑时,可以使用 Converter
  • 示例: 如果你的服务器返回的是 JSON 数据,你可以使用 GsonConverterFactory 将 JSON 数据转换为 Java 对象。
scss 复制代码
Retrofit retrofit = new Retrofit.Builder()
 .baseUrl(BASE_URL)
 .addConverterFactory(GsonConverterFactory.create())
 .build();

源码分析

涉及到的类及重要方法

  • IMyApiService:自定义的 API 接口,通过注解声明网络请求

  • Retrofit:通过内部 Builder.build 构建

    • create方法:通过动态代理,生成 IMyApiService 的代理类;

    • loadServiceMethod方法:ServiceMethod 可以粗浅的理解为我们在 API 接口中定义的方法实现,loadServiceMethod 方法调用缓存或新建的 ServiceMethod 实例

  • ServiceMethod:可以粗浅的理解为我们在 API 接口中定义的方法实现,但它是一个类

    • parseAnnotations方法:创建 RequestFactory 实例,并调用 HttpServiceMethod.parseAnnotations 返回 ServiceMethod 实例。
  • HttpServiceMethod:继承自 ServiceMethod

    • parseAnnotations方法:解析注解的方法,获取所有注解,内部继续调用 createCallAdapter,创建 CallAdapter 对象,最终返回 CallAdapted 内部类或者 SuspendForResponse 内部类(kotlin 协程调用),这两个内部类都继承自 HttpServiceMethod,均包含 requestFactory / callFactory / responseConverter / callAdapter
  • Call/OkHttpCallRetrofit 框架内的 Call 接口,非 OkHttp 内部的 Call

    csharp 复制代码
    public interface Call<T> extends Cloneable {
            Response<T> execute() throws IOException;
            
            void enqueue(Callback<T> callback);
            boolean isExecuted();
            
            void cancel();
            
            boolean isCanceled();
            
            Call<T> clone();
            
            Request request();
            
            Timeout timeout();
      }
    • Call 内部基本上和 OkHttp3.Call 接口一致,有同步执行方法 execute,也有异步网络请求方法 enqueue,这个 Call 接口的实现是 ****OkHttpCall

Retrofit.create

retrofit2.Retrofit#create

typescript 复制代码
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);
            }
          });
}

create 方法中,利用动态(运行期)代理,实现自定义 API 接口的具体代码:

  1. 通过Proxy.newProxyInstance 创建 IMyService 的动态代理实现类;

  2. 当我们调用 apiService.getMediaList("qqm")方法时,实现类内部会调用 ****InvocationHandle ****拦截到对应的方法和参数信息,调用 invoke 方法

  3. invoke 方法中,调用 loadServiceMethod.invoke,这个方法会获取 API 方法上的注解,去拼成一个正常的 OkHttp 请求

retrofit2.Retrofit#loadServiceMethod

sql 复制代码
ServiceMethod<?> loadServiceMethod(Method method) {
  //1.  
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
       //2. 
      result = ServiceMethod.parseAnnotations(this, method);
      // 3.
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}
  1. 从缓存 ConcurrentHashMap 中获取 ServiceMethod

  2. 通过 ServiceMethod.parseAnnotations(this,method),新建 ServiceMethod 对象;

  3. 将新建的 ServiceMethod 实例放入缓存 ConcurrentHashMap 中,以便下次复用

ServiceMethod.parseAnnotations

retrofit2.ServiceMethod#parseAnnotations

scss 复制代码
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  // 1. 
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  ...

 // 2. 
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
  1. 通过 RequestFactory.parseAnnotations(retrofit,method)方法新建 RequestFactory 实例,

    1. RequestFactory可以看成是Retrofit框架中构建OkHttp请求的工厂类,其中包含一个create 方法,用于创建OkHttprequest
dart 复制代码
okhttp3.Request create(Object[] args) throws IOException {
  @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  int argumentCount = args.length;
  if (argumentCount != handlers.length) {
    throw new IllegalArgumentException(
        "Argument count ("
            + argumentCount
            + ") doesn't match expected count ("
            + handlers.length
            + ")");
  }

  RequestBuilder requestBuilder =
      new RequestBuilder(
          httpMethod,
          baseUrl,
          relativeUrl,
          headers,
          contentType,
          hasBody,
          isFormEncoded,
          isMultipart);

  if (isKotlinSuspendFunction) {
    // The Continuation is the last parameter and the handlers array contains null at that index.
    argumentCount--;
  }

  List<Object> argumentList = new ArrayList<>(argumentCount);
  for (int p = 0; p < argumentCount; p++) {
    argumentList.add(args[p]);
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
  1. parseAnnotations:调用内部 Builder.build方法,去解析 API 方法上的注解,解析包括是 GET 还是 POST 方法,Header 有什么,请求链接是什么等,将其赋值给 Builder 内的 headers/method/contentType 等变量,用于后续构建请求体
ini 复制代码
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {   return new Builder(retrofit, method).build(); }

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

// retrofit2.RequestFactory.Builder#build
RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  ...
  
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  }

  ...

  return new RequestFactory(this);
}
  1. HttpServiceMethod.parseAnnotations(retrofit,method,requestFactory)新建 HttpServiceMethod 实例(继承自 ServiceMethod

调用 HttpServiceMethod.parseAnnotations,并将对应的 Retrofit/Method/RequestFactory 对象传入:

ini 复制代码
// retrofit2.HttpServiceMethod#parseAnnotations
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
  boolean continuationWantsResponse = false;
  boolean continuationBodyNullable = false;

  Annotation[] annotations = method.getAnnotations();
  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;
    } else {
      // TODO figure out if type is nullable or not
      // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
      // Find the entry for method
      // Determine if return type is nullable or not
    }

    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } 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);

// 1. 
  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  if (!isKotlinSuspendFunction) {
  // 2. 
    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);
  }
}
  1. 通过 retrofit 获取okhttp3.Call.Factory对象,实际上就是 OkHttpClient 实例

  2. 如果不是 Kotlinsuspend 函数,返回 CallAdapted 对象(HttpServiceMethod 的子类),否则返回 SuspendForBody 对象(同样是 HttpServiceMethod 的子类)

HttpServiceMethod.invoke

追踪了一连串的源码,我们通过 loadService 方法获得了 HttpServiceMethod,而后调用其invoke 方法:

less 复制代码
@Override
final @Nullable ReturnT invoke(Object[] args) {
 //1. 新建OkHttpCall实例
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  //2.
  return adapt(call, args);
}
  1. 新建 OkHttpCall 实例,参数包含前述步骤创建的 RequestFactory 实例, OkHttpClient,响应转换器以及请求参数;

  2. 调用adapt(call,args)方法返回调用结果,该方法有多个实现,我们以 CallAdapted 类为例进行说明:

    typescript 复制代码
    // retrofit2.HttpServiceMethod.CallAdapted#adapt
    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }

调用 callAdapter.adapt(call),没有添加 CallAdapter 时,使用默认的CallAdapter.adapt

typescript 复制代码
//retrofit2.CallAdapter#adapt
@Override
public Call<Object> adapt(Call<Object> call) {
  return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}

返回ExecutorCallbackCall 实例。

至此,就完成了 apiService.getMediaList 的调用

接着,我们调用 Call.enqueue,即ExecutorCallbackCall.enqueue

Call.enqueue

java 复制代码
//retrofit2.DefaultCallAdapterFactory.ExecutorCallbackCall#enqueue
@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(
              () -> {
                if (delegate.isCanceled()) {
          callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                } else {
             callback.onResponse(ExecutorCallbackCall.this, response);
                }
              });
        }

        @Override
        public void onFailure(Call<T> call, final Throwable t) {
    
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}

整个调用还是通过 delegate.enqueue,即 OKHttpCall.enqueue 方法完成:

scss 复制代码
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
      // 1. 
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }


// 2. 
  call.enqueue(
      new okhttp3.Callback() {
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          Response<T> response;
          try {
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);
            return;
          }

          try {
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }

        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
          callFailure(e);
        }

        private void callFailure(Throwable e) {
          try {
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }
      });
}
  1. 通过 createRawCall 创建OkHttp3Call 实例:
java 复制代码
//retrofit2.OkHttpCall#createRawCall
private okhttp3.Call createRawCall() throws IOException {
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

通过 callFactory(即 OkHttpClient)创建 RealCall 对象,即 OkHttp 基本使用的流程。

  1. 得到 RealCall 对象后,调用 RealCall.enqueue 方法,得到响应结果,并在 OkHttpCallbak 中,调用 Retrofit 中的 Callback

至此,整个 Retrofit 的网络请求完成

总结

用户定义注解接口,用于声明Http请求,而Retrofit通过动态代理生成注解接口的代理类,用户发起API调用时,会通过动态代理实现类中的InvocationHandler类中的invoke 方法中,调用loadServiceMethod.invoke,这个方法会获取 API 方法上的注解,去拼成一个正常的 OkHttp 请求,在这个过程中,涉及:

  1. RequestFactory :构建OkHttp Request的工厂类,其中包含parseAnnotations方法用于解析注解;
  2. HttpServiceMethod:可以通过HttpServiceMethod.invoke 获得HttpServiceMethod 实例,而后调用其invoke方法,invoke方法中构建OkHttpCall实例,并返回ExecutorCallbackCall请求结果,我们调用ExecutorCallbackCall.enqueue获得返回结果;
  3. Call.enqueue:其实是OkHttpCall.enqueue的包装,包括返回结果的回调也是对OkHttpCall回调的包装;
相关推荐
Lucky me.1 天前
本地maven添加jar包
okhttp·maven·jar
少说多做3432 天前
Android 网络请求(二)OKHttp网络通信
android·网络·okhttp
粤M温同学4 天前
Android 使用Retrofit 以纯二进制文件流上传文件
android·retrofit
小白学大数据4 天前
使用OkHttp进行HTTPS请求的Kotlin实现
爬虫·python·okhttp·https·kotlin
小白学大数据5 天前
虎扑APP数据采集:JavaScript与AJAX的结合使用
开发语言·javascript·爬虫·ajax·okhttp
柯南二号5 天前
Android okhttp 网络链接各阶段监控
android·网络·okhttp
_Soy_Milk6 天前
动态网页爬取 —— ajax 与 selenium
selenium·ajax·okhttp
陈逸轩*^_^*6 天前
AJAX笔记 (速通精华版)
笔记·ajax·okhttp
Chrison_mu7 天前
Android开发|关于Okhttp发送网络请求
android·网络·okhttp
zpjing~.~7 天前
async 和 await的使用
okhttp