【源码SOLO】Retrofit2源码解析(一)

1,760 阅读10分钟

版权声明:本文为博主原创文章,未经博主允许不得转载
Github:github.com/Darkwh
若有错误或疑问欢迎小伙伴们留言评论

友情提示!!!

本人英文渣,文章中哪些单词翻译的不够形象的话。。。。那你到是来打我呀O(∩_∩)O

系列回顾

【源码SOLO】Retrofit2源码解析(一)

【源码SOLO】Retrofit2源码解析(二)

文章目录

前言

相信大多数Android开发者都听过并使用过Retrofit,一款由square公司开源的网络请求库。自己也用了一段时间,retrofit在okhttp的基础上再一次简化我们的网络请求操作,并且支持rxjava,用起来更加的得心应手。今天给大家分享一下自己阅读retrofit源码的成果,记录的同时希望也能将收获分享给大家,个人水平有限,有错误的地方也请大神包涵和指正。

Retrofit的基本使用

Retrofit的使用方法网上资料很多,这里不多说了,简单贴一下使用方法,以后的分析源码会跟着调用顺序去走。

创建接口文件

interface TestService{
    @GET("data/Android/10/1")
    fun getMsg(): Call<MsgBean>
}

创建接收网络请求的实体类

class MsgBean {
    var error: Boolean = false
}

创建Retrofit对象

val retrofit: Retrofit = Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl("http://gank.io/api/")
                .build()

创建接口的代理对象

val service: TestService = retrofit.create(TestService::class.java)

获取Call对象

val call: Call<MsgBean> = service.getMsg()

执行请求并监听返回结果

call.enqueue(object : Callback<MsgBean> {
            override fun onFailure(call: Call<MsgBean>?, t: Throwable?) {
                Log.i("wh", "onFailure")
            }
            override fun onResponse(call: Call<MsgBean>?, response: Response<MsgBean>?) {
                Log.i("wh", "onResponse")
            }
        })

源码目录结构及项目组成

源码目录

retrofit的源码类比较少,非常适合新手阅读(比如我),其中http包下面都是一些自定义的注解(@GET、@POST等),这里就不一一列出了

比较重要的几个类

接口:Call、CallAdapter、Converter、Callback

类:Retrofit、ServiceMethod、ParameterHandler、OkHttpCall


接下来让我们了解一下这几个类大致是做什么的,首先我们从接口开始,因为接口属于高度抽象,了解接口的定义有助于我们理解源码。

1.Call

/**
 * An invocation of a Retrofit method that sends a request to a webserver and returns a response.
 * Each call yields its own HTTP request and response pair. Use {@link #clone} to make multiple
 * calls with the same parameters to the same webserver; this may be used to implement polling or
 * to retry a failed call.
 *
 * <p>Calls may be executed synchronously with {@link #execute}, or asynchronously with {@link
 * #enqueue}. In either case the call can be canceled at any time with {@link #cancel}. A call that
 * is busy writing its request or reading its response may receive a {@link IOException}; this is
 * working as designed.
 *
 * @param <T> Successful response body type.
 */
public interface Call<T> extends Cloneable {
    ......
}

一个向服务器发送请求并获得响应的调用器,主要的方法为

Response<T> execute() throws IOException;

void enqueue(Callback<T> callback);

这两个方法小伙伴们应该很熟悉了,execute是发送同步请求,enqueue是发送异步请求

2.CallAdapter

/**
 * Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
 * created by {@linkplain Factory a factory} which is
 * {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit}
 * instance.
 */
public interface CallAdapter<R, T> {
    ......
}

中文意思大致就是说CallAdapter接收一个Call对象(带着返回类型参数R)并转换为新的类型T。CallAdapter会被 Retrofit.Builder.addCallAdapterFactory(Factory)指定的Factory创建(注意builder可以指定多个Factory的,后面会说明这个问题)

上面的文字可能会让你觉得比较乱,没关系我们来看一下这个接口中的其中一个抽象方法:

T adapt(Call<R> call);

这个方法就是CallAdapter最为核心的一个方法,如同注释中的描述一样,接收一个Call对象,Call对象带着参数R,方法返回T类型。

再看CallAdapter接口中的另一个方法:

Type responseType();

代码中的注解描述为:当把HTTP返回体转换为JAVA类的时候返回参数类型,例如你得自定义接口中的方法返回值为Call<Foo>,那么Type类型则为Foo。

总结来说CallAdapter将Call<R> 转换为T的这么一个转换适配器

CallAdapter接口中还有一个内部工厂类:

 abstract class Factory {
    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }

典型的工厂模式,通过get方法来获取CallAdapter实例

3.Converter

/**
 * Convert objects to and from their representation in HTTP. Instances are created by {@linkplain
 * Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}
 * into the {@link Retrofit} instance.
 */
public interface Converter<F, T> {
    ......
}

Converter接口的作用是将Java实体类和Http内容相互转换
同CallAdapter一样,Converter接口中也有一个内部工厂类:

  abstract class Factory {
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
  • responseBodyConverter方法将Http内容转换为Java实体
  • requestBodyConverter方法将Java实体转换为Http内容(RequestBody)

4.Callback

public interface Callback<T> {

  void onResponse(Call<T> call, Response<T> response);

  void onFailure(Call<T> call, Throwable t);
}

网络请求成功/失败的回调接口,不多说了吧就。

以上为Retrofit源码中的四个接口及其简单介绍。接下来让我们再了解一下其他几个比较重要的类

1.Retrofit

就这个名字,和项目名同款,意味这个类的地位是多么重要,具体代码后面分析。

2.ServiceMethod

/**
 * Adapts an invocation of an interface method into an HTTP call.
 */
final class ServiceMethod<R, T> {
    ......
}

将一个接口方法转换为HTTP请求的调用器
非常核心的一个类,它的作用同源码中的注释一样,会将你定义的接口中的方法转换为Http请求。具体的代码我们同样放到后面分析。

3.ParameterHandler

这个类源码没什么注释,故名思议是处理参数的,这里处理的是请求参数。
ParameterHandler的定义如下

abstract class ParameterHandler<T> {

    abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
  
    final ParameterHandler<Iterable<T>> iterable() {
    return new ParameterHandler<Iterable<T>>() {
      @Override void apply(RequestBuilder builder, @Nullable Iterable<T> values)
          throws IOException {
        if (values == null) return; // Skip null values.

        for (T value : values) {
          ParameterHandler.this.apply(builder, value);
        }
      }
    };
  }

  final ParameterHandler<Object> array() {
    return new ParameterHandler<Object>() {
      @Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
        if (values == null) return; // Skip null values.

        for (int i = 0, size = Array.getLength(values); i < size; i++) {
          //noinspection unchecked
          ParameterHandler.this.apply(builder, (T) Array.get(values, i));
        }
      }
    };
  }
}

ParameterHandler是一个抽象类,拥有一个抽象方法apply和两个遍历方法,两个遍历方法分别适用于Iterable 和数组,遍历集合(数组)并执行apply方法。

ParameterHandler有许多的子类,我们来看其中的几个:

  static final class RelativeUrl extends ParameterHandler<Object> {
    @Override void apply(RequestBuilder builder, @Nullable Object value) {
      checkNotNull(value, "@Url parameter is null.");
      builder.setRelativeUrl(value);
    }
  }
  static final class Header<T> extends ParameterHandler<T> {
  private final String name;
  private final Converter<T, String> valueConverter;

  Header(String name, Converter<T, String> valueConverter) {
    this.name = checkNotNull(name, "name == null");
    this.valueConverter = valueConverter;
  }

  @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
    if (value == null) return; // Skip null values.

    String headerValue = valueConverter.convert(value);
    if (headerValue == null) return; // Skip converted but null values.

    builder.addHeader(name, headerValue);
  }
}
static final class Part<T> extends ParameterHandler<T> {
  private final Headers headers;
  private final Converter<T, RequestBody> converter;

  Part(Headers headers, Converter<T, RequestBody> converter) {
    this.headers = headers;
    this.converter = converter;
  }

  @Override void apply(RequestBuilder builder, @Nullable T value) {
    if (value == null) return; // Skip null values.

    RequestBody body;
    try {
      body = converter.convert(value);
    } catch (IOException e) {
      throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
    }
    builder.addPart(headers, body);
  }
}

不难看出,ParameterHandler作用就是为RequestBuilder构建参数,RequestBuilder是retrofit源码中的一个累,其build方法会返回一个Request对象,Request对象是OkHttp中的类,没看过,就不分(装)析(逼)了。

不知道小伙伴们有没有注意到Part(Headers headers, Converter<T, RequestBody> converter)这行代码,这就是前面说的Converter接口其中一个作用,将Java对象转换为Http内容。

3.OkHttpCall

这是Retrofit中对Call接口内置的默认实现,这个类其实并没有Retrofit和ServiceMethod重要,考虑是默认实现,所以也会着重分析一下,来让我们SOLO下代码:

嗯。。。代码有点多,就不贴了,还是来让我们看看其中比较重要的几个变量和方法吧:

private final ServiceMethod<T, ?> serviceMethod;

private @Nullable
okhttp3.Call rawCall;

OkHttpCall对象持有一个ServiceMethod引用和一个okhttp3.Call引用

再看OkHttpCall的其他方法,这里我们主要看一下enqueue异步请求方法,其它代码小伙伴们可自行研究:

    @Override
    public void enqueue(final Callback<T> callback) {
        //检查传入的Callback是否为空,为空抛出异常
        checkNotNull(callback, "callback == null");
        okhttp3.Call call;
        Throwable failure;
        synchronized (this) {
            //此OkHttpCall对象如果执行过,再次调用会抛出异常
            //一般不会抛这个异常,因为每次都是创建一个新的OkHttpCall对象
            //所以源代码中并没有executed=false这种设置,只有executed = true
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;
            call = rawCall;
            failure = creationFailure;
            if (call == null && failure == null) {
                try {
                    //此处创建原始的Call对象
                    call = rawCall = createRawCall();
                } catch (Throwable t) {
                    //如果为指定的三种异常,则抛出异常(为RxJava支持)
                    throwIfFatal(t);
                    //变量赋值
                    failure = creationFailure = t;
                }
            }
        }
        if (failure != null) {
            //回调给用户设置的匿名接口
            callback.onFailure(this, failure);
            return;
        }
        if (canceled) {
            call.cancel();
        }
        //调用OkHttp的Call对象的enqueue方法
        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
                    throws IOException {
                Response<T> response;
                try {
                    //解析响应参数
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    //如有解析异常则回调给callback
                    callFailure(e);
                    return;
                }
                callSuccess(response);
            }

            @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) {
                    t.printStackTrace();
                }
            }

            private void callSuccess(Response<T> response) {
                try {
                    callback.onResponse(OkHttpCall.this, response);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }

enqueue方法总结来说是首先创建一个原始的Call对象(注意这里为okhttp3的Call对象),然后调用Call.enqueue方法,并对返回结果进行解析,请求出错或者解析出错会回调到你设置的Callback对象中的onFailure方法,解析成功则会回调callSuccess方法

来看一下创建原始Call对象的方法定义:

private okhttp3.Call createRawCall() throws IOException {
       Request request = serviceMethod.toRequest(args);
       okhttp3.Call call = serviceMethod.callFactory.newCall(request);
       if (call == null) {
           throw new NullPointerException("Call.Factory returned null.");
       }
       return call;
   }

通过ServiceMethod的toRequest方法构造出一个Request队形,然后通过指定的callFactory来创建call对象并返回。这里默认的callFactory前面讲解Retrofit.Builder的build方法中有介绍,未指定的时候默认使用OkHttpClient。
看一下toRequest方法的定义:

    /**
     * Builds an HTTP request from method arguments.
     */
    Request toRequest(@Nullable Object... args) throws IOException {
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
                contentType, hasBody, isFormEncoded, isMultipart);

        @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
                ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

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

        for (int p = 0; p < argumentCount; p++) {
            handlers[p].apply(requestBuilder, args[p]);
        }

        return requestBuilder.build();
    }

这个方法的作用就是通过遍历上面介绍过的ParameterHandler数组并调用apply函数将方法中的参数转换成Http请求参数。

再来看一下解析返回结果的方法定义:

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();

        ......

        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
            //解析参数
            T body = serviceMethod.toResponse(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;
        }
    }

其中会调用ServiceMethod.toResponse,稍微跟一下:

    R toResponse(ResponseBody body) throws IOException {
        return responseConverter.convert(body);
    }

可以看到此处使用你指定的Convert来对响应数据进行转换(比如我们最常用的json转实体类)

总结

以上为大家介绍了一下retrofit的基本使用和几个重要的类,下一篇我将按照retrofit使用的顺序来分析一下源码,将我说理解的内容分先给大家,如有错误的地方还请包涵和指正!勿喷!!!