1.基本用法

  • 创建接口
public interface GitHubService {
    @GET("users/{user}/repos")
    Observable<List<Repo>> listRepos(@Path("user") String user);
}
  • 创建Retrofit实例

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("https://api.github.com")
  .addConverterFactory(GsonConverterFactory.create())
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  .build();
  • 生成接口实现类

GitHubService service = retrofit.create(GitHubService.class);
  • 调用接口实现类的请求方法,获取Call对象

Call<List<Repo>> repos = service.listRepos("octocat");
  • 调用Call对象的异步执行方法

repos.enqueue(new Callback<List<Repo>>() {
   @Override
   public void onResponse(Response<List<Repo>> response) {
     // do something  
   }
   @Override
   public void onFailure(Throwable t) {
     // do something
   }
});

2.主线剧情的源码

  • 构造接口

通过注解的方式来构造接口,以GET方法为例:


@Documented
@Target(METHOD)//注解用于方法
@Retention(RUNTIME)//该注解运行时可用(TIJ P622)
public @interface GET {
  String value() default "";
}
  • 创建Retrofit实例

此处使用了建造者模式,仅以baseurl为例,其余代码省略


public static final class Builder {
    private Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    //...
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

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

可以看见有两个List,ConverterFactories和adapterFactories。

在配置它们的时候,使用了保护性拷贝


// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

这是后文的重要角色,Retrofit为了解耦合,使用了CallAdapter用来做底层网络请求适配,以及Converter做数据格式的适配。

Converter分为requestConverter、responseConverter和StringConverter三种,可以对请求和响应数据进行包装转换,在定义Retrofit对象时可以自己制定。

在请求时Retrofit使用的是泛型,当把Http Response装换成Java对象时,利用Converter将其转化为请求原本需要的数据类型,完成不用数据格式的适配。

关于Converter我会后续再专门写一篇,也可以阅读以下下面的博客

Retrofit 2.0 源码解析

Retrofit2源码分析(一)

  • 生成接口实现类

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

        @Override public Object invoke(Object proxy, Method method, 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);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

这里就使用了动态代理

  • 动态代理

动态代理机制是Java的一个高级特性, 其主要功能就是可以为委托类对象生成代理类, 代理类可以将所有的方法调用分派到委托对象上反射执行. 动态代理的相关知识可参考 相关的Java书籍.
这里传入newProxyInstance()有三个参数:

  1. 接口的classLoader.
  2. 只包含接口的class数组.
  3. 自定义的InvocationHandler()对象, 该对象实现了invoke() 函数, 通常在该函数中实现对委托类函数的访问.

分析一下上面的代码:

首先做了两个验证,验证接口和标识。
如果我们调用的是来自 Object 类或者平台默认的方法,则会交给方法执行或者平台执行,但从代码上看 isDefaultMethod(method) 直接返回的是 false,可能是为了方便开发者扩展设置各个平台的不同方法调用。
经过两个判断后,会将我们的方法转换成一个 ServiceMethod对象,这是 Retrofit 中最核心的一个类,通过 ServiceMethod 中存储的请求信息向网络方向可以根据 Call 构建出真实的请求实体进行网络请求。

看一下loadServiceMethod方法的实现:


ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

下面是ServiceBuilder的一部分代码:


static final class Builder<T> {
    //...
    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      //...
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
      //...
    }
    //...
}

可见,我们通过解析注解的方式来生成ServiceMethod对象,但是解析注解不是效率很低么?

对,这里为了解决效率问题,使用了ServiceMethodCache,利用缓存获取已经解析过注解的ServiceMethod。

最后看看最核心的三行代码:


ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

生成接下来创建了一个 OkHttpCall。

并使用 serviceMethod.CallAdapter 对 OkHttpCall 进行了转化。

这里看起来有点绕,用ServiceMethod生成了OkHttpCall后,还要用serviceMethod去adapt它,形成了一个闭环,其实可以将这里理解为一种跟踪。

而callAdapter是用来做类型转换的,将call对象转换成合适的网络请求方式,默认是OKHttp,这里的设计也是为了方便与RxJava相结合,将OKHttpCall进行一次封装,编程Retrofit的Call,如果你想的话,也可以自己写一个HTTPURLConnectionCall,然后再封装成Retrofit的call。

在 Android 平台,我们使用了 ExecutorCallAdapterFactory 封装网络请求为一个 Call 并将请求结果转发到主线程

Retrofit.create()实际上对 REST 服务接口做了一个动态代理,并返回一个实例,用来完成实际的 http 请求。

  • 获取Call对象

直接按需调用我们声明出来的服务接口实例的自定义方法即可,此时Retrofit会为通过动态代理你生成一个需要的HTTP请求——Call

  • 执行请求

获得Call一般式OKHttpCall

OKHttpCall是用来操作请求的,包括同步执行(execute),异步执行(enqueue),取消请求等功能。Retrofit最新版本默认使用OKHttp去做网络请求。

最后会回调我们定义好的CallBack接口中的\onResponse或者onFailture

到此为止,主线任务上的源码逻辑就理清了。

Retrofit源码1: 为什么写一个interface就可以实现http请求

用 Retrofit 2 简化 HTTP 请求

Retrofit 源码分析之执行流程

posted on 2016-06-14 16:52  岳阳楼  阅读(989)  评论(0编辑  收藏  举报