Retrofit源码分析

版本号:2.5.0

一.基本使用

1.定义请求接口

interface GithubService {
    //通过注解定义请求的方法以及路径,“{}”里面的表示:该内容是可变的,通过下面方法的参数赋值
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<Any>
}

2.创建Retrofit对象,通过Call对象发送网络请求

//创建Retrofit对象
val retrofit = Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//设置支持Rxjava平台
        .build()
//创建网络请求接口实例
val githubService = retrofit.create(GithubService::class.java)
//调用相应的接口获取对应的call对象
val call = githubService.listRepos("user")
//通过call对象执行异步请求
call.enqueue(object : Callback<Any> {
    override fun onFailure(call: Call<Any>, t: Throwable) { }
    override fun onResponse(call: Call<Any>, response: Response<Any>) {  }
})

通过上面的代码可以看出,真正发送网络请求的是Call对象,也就是OkhttpRetrofit只是对网络请求参数的封装,真正的请求是通过Okhttp完成的。

应用通过Retrofit发送网络请求,实际上是使用Retrofit接口层封装请求参数,之后由Okhttp完成后续的请求操作,在服务端返回数据之后,Okhttp将原始的结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。

Okhttp源码分析

二.源码分析

1.创建对象

网络请求参数的封装是通过Retrofit完成的,所以先看一下给对象的创建代码:
Retrofit.Builder()

public static final class Builder {
    //Retrofit支持平台
    private final Platform platform;
    //网路请求的okhttp的工厂,默认就是OkhttpClient
    private @Nullable okhttp3.Call.Factory callFactory;
    //请求的基地址
    private @Nullable HttpUrl baseUrl;
    //数据转换器工厂集合;数据转换器就是将从网络获取的数据转成java对象
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    //适配器工厂集合;适配器工厂:将我们Call对象转换成其他类型能用的请求,比如:Rxjava
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    //用于执行异步回调的,在Android是默认在主线程中回调
    private @Nullable Executor callbackExecutor;
    //标志位,用于后面是否立即解析方法参数
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

     public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //其他的成员变量判断
      ......
      //创建Retrofit对象,该对象中也有对应的成员变量,除了platform
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

}

重点关注一下成员变量,如注释所示。在上述代码中Builder()会调用Builder(Platform platform)传入一个Platform对象,下面看一个该对象创建的代码:Platform.get()

class Platform {
    private static final Platform PLATFORM = findPlatform();
    static Platform get() {
    return PLATFORM;
    }

    private static Platform findPlatform() {
    try {
      //加载指定的类
      Class.forName("android.os.Build");
      //如果Build.VERSION.SDK_INT != 0,说明是Android平台
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } ......
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    }......
    return new Platform();
}

从上述代码可以看出,Retrofit支持AndroidJava8平台。这里我们只关注Android平台,下面看一下Android中做了什么。

static class Android extends Platform {
    ......
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }
    ......
    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }
    ......
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());
      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
}

Android中提供了默认的Executor、ConverterFactor和CallAdapterFactory,其中默认的异步异步回调处理是在AndroidUI线程处理的new Handler(Looper.getMainLooper());,这也就是为什么Retrofit网络请求回调是在主线程中执行的(Okhttp的回调是在子线程中执行的)

2.参数设置

baseUrl()

public Builder baseUrl(String baseUrl) {
     checkNotNull(baseUrl, "baseUrl == null");
     //HttpUrl对baseUrl进行解析分段,如获取:scheme、host、port和PathSegments
     return baseUrl(HttpUrl.get(baseUrl));
}

public Builder baseUrl(HttpUrl baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    List<String> pathSegments = baseUrl.pathSegments();
    //baseUrl的必须以“/”结尾
    if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
    }
    this.baseUrl = baseUrl;
    return this;
}

注意:baseUrl的必须以“/”结尾,否则抛出异常IllegalArgumentException(baseUrl must end in /:)

addConverterFactory()

public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
}

该方法很简单,就是把设置的工厂添加到对应的集合中,重点看一下Converter.Factory对象的创建:GsonConverterFactory.create()

public static GsonConverterFactory create() {
    return create(new Gson());
}

public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
}

private GsonConverterFactory(Gson gson) {
    this.gson = gson;
}

创建的过程就是创建一个Gson对象,然后给GsonConverterFactory的属性gson赋值。

addCallAdapterFactory()

该方法同addConverterFactory()类似,将Factory对象添加到相应的集合中。RxJava2CallAdapterFactory.create()方法也类似,创建一个RxJavaScheduler对象并赋值。

3.获取请求接口实例

retrofit.create(GithubService::class.java)

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(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];

          //proxy:代理对象
          //method:调用的方法信息
          //args:调用方法的参数
          //当代理对象方法调用的时候就会调用该方法
          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // 如果调用的是Object类中的方法,比如:toString()方法等
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //如果是平台默认的方法,Android平台该方法返回false,所以if条件不成立。
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //最终会调用该方法
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
}

Android平台下调用请求方法时,是通过动态代理来实现的,在这个过程中最终会调用loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

我们先看一下loadServiceMethod(method)方法

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;
}

loadServiceMethod方法就是返回了一个ServiceMethod对象,该对象是对请求方法method的封装,如果缓存中存在就从缓存中获取,否则直接创建一个:ServiceMethod.parseAnnotations(this, method);ServiceMethod是一个抽象类,调用的是它子类HttpServiceMethodparseAnnotations方法,在该方法中直接new HttpServiceMethod对象,下面就看一下该类中的相关属性和方法:

final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
  ......
  //请求工厂,里面封装着请求方法先关的信息
  private final RequestFactory requestFactory;
  //请求工厂:其实就是OkhttpClient
  private final okhttp3.Call.Factory callFactory;
  private final CallAdapter<ResponseT, ReturnT> callAdapter;
  private final Converter<ResponseBody, ResponseT> responseConverter;
  ......

  @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
}

下面重点看一下RequestFactory

final class RequestFactory {
    ......
    private final Method method;
    private final HttpUrl baseUrl;
    final String httpMethod;
    private final @Nullable String relativeUrl;
    private final @Nullable Headers headers;
    private final @Nullable MediaType contentType;
    private final boolean hasBody;
    private final boolean isFormEncoded;
    private final boolean isMultipart;
    //参数解析器
    private final ParameterHandler<?>[] parameterHandlers;
    ......
}

RequestFactory的成员变量可以看出该类中包括请求相关的所有信息。

创建好ServiceMethod对象以后,调用该对象的invoke方法:

@Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}

该方法就会返回一个Call对象,allAdapter.adapt()方法就是将OkHttpCall对象转换成其他平台能用的Call对象,比如:RxJava。这里返回的是OkHttpCall的对象,该类是Retrofit中定义的,是对Okhttp中的Call对象的封装。下面看一下该类中的相关属性和方法:

final class OkHttpCall<T> implements Call<T> {
    private final RequestFactory requestFactory;
    private final Object[] args;
    private final okhttp3.Call.Factory callFactory;
    private final Converter<ResponseBody, T> responseConverter;

    private volatile boolean canceled;

    @GuardedBy("this")
    private @Nullable okhttp3.Call rawCall;
    ......
    //异步请求方法
    @Override public void enqueue(final Callback<T> callback) {
        ......
        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 {
              call = rawCall = createRawCall();
            }......
          }
        }
        ......
        //调用Okhttp-》call的enqueue方法,完成网络请求
        call.enqueue(new okhttp3.Callback() {
            @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                Response<T> response;
                try {
                  //解析响应数据
                  response = parseResponse(rawResponse);
                }......
                
        }
    }
    ......
    //创建Call对象:okHttpClient.newCall(request)
    private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        ......
        return call;
    }

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

        int code = rawResponse.code();
        //code判断
        if (code < 200 || code >= 300) {
          try {
            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);
        }......
    }
}

OkHttpCallOkhttpCall封装了一下,它里面的enqueue方法其实调用的就Call.enqueue,在它的回调成功方法中,通过配置的Converter.Factory将响应数据转换成对应的实体类(Java对象)。还有一个需要关注的地方就是,Requst的创建,是通过requestFactory.create(args)创建的,下面看一下该方法:

final class RequestFactory {
    ......
    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 + ")");
        }
        //通过Builder模式创建Request对象
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);

        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();
    }
    ......
}

该方法就是根据ParameterHandler解析的参数以及传入的参数值args创建Request对象

总结

在调用Retrofitcreate方法获取接口请求实例Call对象,内部使用的是动态代理的方式,在调用相应的网络请求方法的时候,会回调invoke方法,在该方法中会调用ServiceMethod()invoke方法。
ServiceMethod是一个抽象类,实际使用的是HttpServiceMethod,该类是对请求方法信息的封装,里面有几个比较重要的成员变量:

  • 1.RequestFactory :包含请求相关的信息
  private final Method method;
  private final HttpUrl baseUrl;
  final String httpMethod;
  private final @Nullable String relativeUrl;
  private final @Nullable Headers headers;
  private final @Nullable MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  //参数解析器
  private final ParameterHandler<?>[] parameterHandlers;
  • 2.okhttp3.Call.Factory
  • 3.callAdapter
  • 4.responseConverter

ServiceMethod.invoke的方法会返回一个OkHttpCall对象,该对象是对OkHttpCall的封装。真正的网络请求是通过OkHttpCall来实现的。

Kotlin项目实战

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342