[开源解码]Retrofit源码解析

1,667 阅读11分钟

Retrofit官方文档

A type-safe HTTP client for Android and Java

概述

  • Retrofit本身并不是一个网络请求框架,而是一个网络请求框架的封装
  • App应用程序通过 Retrofit请求网络,实际上是使用Retrofit 接口层封装请求参数,之后由OkHttp完成后续的请求操作
  • 在服务端返回数据之后,OkHttp 将原始的结果交给Retrofit, Retrofit根据用户的需求对结果进行解析

类型安全

  • 即Retrofit可以在编译期检查出错误,比如AS编码警示
  • 不会在运行是出现错误

使用

  • 添加Retrofit库的依赖,
  • 添加网络权限创建接收服务器返回数据的类(实体Bean)
  • 创建用于描述网络请求的接口
  • 创建Retrofit实例
  • 创建网络请求接口实例
  • 发送网络请求(异步/同步)
  • 处理服务器返回的数据

代码

  • 1.创建一个interface作为WebService的请求集合,在里面用注解(Annotation)写入需要配置的请求方法
    interface GitHubService {
      @GET("users/{user}/repos")
      fun listRepos(@Path("user") user: String?): Call<List<Repo>>
    
      //使用http注解
      @HTTP(method = "get",path = "users/{user}/repos",hasBody = false)
      fun httpRepos(@Path("user") user: String?): Call<List<Repo>>
    }
    
  • 2.在正式代码里用 Retrofit 创建出 interface 的实例
  • 3.调用创建出的Service实例的对应方法,创建出相应的可以用来发起网络请求的Call对象
  • 4.使用 Call.execute() 或者 Call.enqueue() 来发起请求
    class RetrofitActivity:AppCompatActivity() {
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_retrofit);
    
        //1 构建一个retrofit对象
        val retrofit = Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RXJava支持
            .build()
    
        //2. 获取接口的代理对象,传入字节码
        val service = retrofit.create(GitHubService::class.java)
    
        //3 获取具体的请求业务方法
        val repos: Call<List<Repo>> = service.listRepos("octocat")
    
        //4 发起异步请求
        repos.enqueue(object : Callback<List<Repo>?> {
          override fun onFailure(call: Call<List<Repo>?>, t: Throwable) {
            println("Response: ${t.message}")
          }
    
          override fun onResponse(call: Call<List<Repo>?>, response: Response<List<Repo>?>) {
            println("Response: ${response.body()!![0].name}")
          }
        })
        //同步的,不切换线程(安卓中不使用)
    //    repos.execute()
    
        val listReposRx = service.listReposRx("octocat")
        listReposRx.subscribe()
    
      }
    }
    

静态代理与动态代理

代理模式: 为其他对象提供一种代理,用以控制对这个对象的访问

静态代理

//抽象类
abstract class AbstractObject {
    protected abstract void operation();
}

//真实对象
class RealObject extends AbstractObject {
    @Override
    protected void operation() {
        System.out.println("Do something");
    }
}

//代理对象
class ProxyObject extends AbstractObject {

    //对目标类的引用
    RealObject realObject;

    public ProxyObject(RealObject realObject) {
        this.realObject = realObject;
    }

    //可以在真实目标对象操作前后加需要的逻辑
    @Override
    protected void operation() {
        System.out.println("do something before real operation");
        if (realObject == null){
            realObject = new RealObject();
        }
        realObject.operation();
        System.out.println("do something before after operation");
    }
}

动态代理

动态代理:代理类在程序运行时创建的代理方式,根据在代码中的配置动态生成。相比于静态代理,动态代理能够很方便地对代理类的函数做统一的处理,而不用频繁修改每一个代理类的函数。

  • 无侵入
  • 方法增强

分类

  • jdk动态代理:不同于静态代理,jdk动态代理只能为接口创建代理对象
  • CGLIB

jdk动态代理举例

InvocationHandler

  • 每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现
  • invoke方法的参数中可以获取参数
  • invoke方法的返回值被返回给使用者

步骤

  • 创建接口
  • 创建真实对象
  • 创建代理类
  • 客户端使用
    • 通过java.lang.reflect.Proxy.newProxyInstance(...)方法获取真实对象的代理对象
    • 通过代理对象调用真实对象相关接口实现的方法,这个时候就会跳转到这个代理对象所关联的handler的invoke()方法
//1. 创建接口
interface Subject {
    void shoping();
}
//2. 创建真实对象
class Man implements Subject{
    @Override public void shoping() {
        System.out.println("DSH 要去买东西");
    }
}
//3.创建代理类
class Proxy implements InvocationHandler {
    private Object target;//要代理的真实对象
    public Proxy(Object target) {
        this.target = target;
    }
    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy: "+proxy.getClass().getName());
        System.out.println("before...");
        method.invoke(target,args);
        System.out.println("after");
        return null;
    }
}
//4. 客户端调用
class Client {
    public static void main(String[] args) {
        Subject man = new Man();
        Proxy p = new Proxy(man);
        //通过java.lang.reflect.Proxy.newProxyInstance(...)方法获取真实对象的代理对象
        Subject subject = (Subject) java.lang.reflect.Proxy.
                newProxyInstance(man.getClass().getClassLoader(),
                        man.getClass().getInterfaces(),
                        p);
        //通过代理对象调用真实对象相关接口实现的方法,
        // 这个时候就会跳转到这个代理对象所关联的handler的invoke()方法
        subject.shoping();
        //获得真实对象的代理对象所对应的Class对象的名称,用字符串表示
        System.out.println(subject.getClass().getName());

        //输出结果
        //proxy: com.dsh.imocc.proxy.dynamic.$Proxy0
        //before...
        //DSH 要去买东西
        //after
        //com.dsh.imocc.proxy.dynamic.$Proxy0
    }
}

框架构成

网络请求的八个步骤及七个关键的成员变量

八个步骤

  1. 创建retrofit实例
  2. 定义一个网络请求接口并为接口中的方法添加注解
  3. 通过动态代理生成网络请求对象
  4. 通过网络请求适配器将网络请求对象进行平台适配
  5. 通过网络请求执行器发送网络请求
  6. 通过数据转换器解析数据
  7. 通过回调执行器切换线程
  8. 用户在主线程处理返回结果

七个关键成员变量

public final class Retrofit {
...
	//1. 缓存
  	private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
    //2. 请求工厂 默认是OkhttpClient
  	final okhttp3.Call.Factory callFactory;
    //3. 基地址
  	final HttpUrl baseUrl;
    //4. 数据转换器工厂集合
  	final List<Converter.Factory> converterFactories;
    //5. 网络请求适配器工厂集合
  	final List<CallAdapter.Factory> callAdapterFactories;
    //6. 线程回调 在安卓中默认是主线程 
  	final @Nullable Executor callbackExecutor;
    //7. 标志位 是否立即解析接口中的方法
  	final boolean validateEagerly;
...  
}
  • retrofit网络请求默认返回Call,添加ConvertFactory之后可以支持自定义实体Call,添加CallAdapterFactory之后支持自定义返回类型Observable,这也是我们开发中常见的方式

retroft中builder构建者模式&builder内部类解析

构造器中通过Platform.get()创建了默认的转换器、适配器、线程调度器等

val retrofit = Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RXJava支持
        .build()
  • baseUrl:将String类型的url转换为HttpUrl对象
  • addConverterFactory:设置数据解析器,GsonConverterFactory.create()就是一个Gson的赋值操作
  • addCallAdapterFactory:数据适配器工厂,原理同上

build()方法

源码分析

1.Retrofit.create方法

  • 通过 Retrofit.create(Class) 方法创建出 Service interface 的实例,从 而使得 Service 中配置的方法变得可用,这是 Retrofit 代码结构的核心; retrofit.create(GitHubService::class.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.
            //如果是Object类自有的方法就直接执行
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //如果是平台中自有的方法也直接执行
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //call就做了两件事
            //一:生成一个真正的OKHTTP的call并且加入请求队列
            //二:对返回结果利用之前定义的converter转换后返回给callback
            //之所以会用adapter的adapt方法主要是为了线程切换(rxjava的转换),这里是把结果切回主线程
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

1.1 验证服务的接口:validateServiceInterface()

验证是否为接口而且是不继承其他接口的接口 1

1.2 Proxy.newProxyInstance():动态代理创建 Service接口 实例。

  • Retrofit.create() 方法内部,使用的是Proxy.newProxyInstance() 方法来创建 Service 实例。
  • 这个方法会为参 数中的多个 interface (具体到 Retrofit 来说,是固定传入一个 interface)创建 一个对象,这个对象实现了所有 interface 的每个方法,并且每个方法的实现都 是雷同的:调用对象实例内部的一个 InvocationHandler 成员变量的invoke() 方法,并把自己的方法信息传递进去。
  • 这样就在实质上实现了代理 逻辑:interface 中的方法全部由一个另外设定的 InvocationHandler 对象 来进行代理操作。并且,这些方法的具体实现是在运行时生成 interface 实例时 才确定的,而不是在编译时(虽然在编译时就已经可以通过代码逻辑推断出 来)。这就是网上所说的「动态代理机制」的具体含义。

通过动态代理实际生成了类似如下的实际运行代码

class ProxyGithubService implements GitHubService{
    InvocationHandler invocationHandler = 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 (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
                return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
    };

    @NotNull @Override
    public Call<List<Repo>> listRepos(@org.jetbrains.annotations.Nullable String user) {
        Method method = null;
        try {
            method = GitHubService.class.getDeclaredMethod("listRepos",String.class);
            return (Call<List<Repo>>) invocationHandler.invoke(this,method, new String[] { user });
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

因此, invoke() 方法中的逻辑,就是 Retrofit 创建 Service 实例的关键。

2.invoke核心方法loadServiceMethod()

因为上面这些核心的就是动态代理,它会代理service中的每个方法,既然是代理接口类的每个方法,那么首先是找到方法 loadServiceMethod(method);

2.1 ServiceMethod 的创建:

  • 核心是ServiceMethod.parseAnnotations
  • 这行代码负责读取 interface 中原方法的信息(包括返回值类型、方法注解、参 数类型、参数注解),并将这些信息做初步分析。实际返回的是一个CallAdapted 。
    • 首先从之前加载过的缓存中取,
    • 没加载过就重新构建并加入到缓存中
  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,分步解析 如下2.2 2.3

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

2.2 解析注解->RequestFactory.parseAnnotations->RequestFactory生成

在retrofit.create()中loadServiceMethod的时候,RequestFactory通过Builder构造一个RequestFactory对象并返回,之后用于网络请求

  • build过程包含parseMethodAnnotation(annotation)、parseParameter、parseHeader等,最后生成请求和返回所需要的全部参数 注解流程
retrofit注解流程

2.3 CallAdapted对象生成->HttpServiceMethod.parseAnnotations

  • ServiceMethod.parseAnnotations(this, method) -> HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)
  • 这里实际上构造了一个CallAdapted对象
    ...
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }
    ...
    

2.4 OkHttpCall 的创建:

loadServiceMethod(method)返回了一个CallAdapted对象之后,就开始执行invoke()这个方法

loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

继续追踪源码,于是到了HttpServiceMethod中来

  • OkHttpCall 是 retrofit2.Call 的子类。
  • 这行代码负责将 ServiceMethod 解读到的信息(主要是一个 RequestFactory 、一个OkHttpClient 和一个 ResponseConverter )封装进 OkHttpCall ;
  • 而这个对象可以在需要的时候(例如它的 enqueue() 方法被调用的时候), 利用 RequestFactory 和 OkHttpClient 来创建一个 okhttp3.Call 对象,并调用这个 okhttp3.Call 对象来进行网络请求的发起,比如异步的enquue。
  • 然后利用ResponseConverter 对结果进行预处理之后,交回给 Retrofit 的 Callback 。
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {

    ...

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

2.5 OkHttpCall的enqueue()方法

  • 下面代码中最重要的则是createRawCall(),也就是创建OKHTTP的call,然后利用这个call调用OKHTTP的enqueque来进行真正的网络请求,同时将返回值返回到callback中。

    @Override 
    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. createRawCall 创建okhttp的call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }
    
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    
    if (canceled) {
      call.cancel();
    }
    
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          //2. 解析response
          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
        }
      }
    });
    }
    
  • 到这里已经可以拿到网络通信结果了当然是通过 parseResponse(rawResponse) 转换的最终结果,而这个转换则是通过初始化时 addConverterFactory 添加的转换器

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
    
    ...
    
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      ...
    }
    }
    
  • responseConverter来源

    • 追踪源码,responseConverter来自于HttpServiceMethod中CallAdapted创建之前

2.6 adapt() 方法:

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);中拿到了okhttp请求结果之后,接下来继续执行adapt(call, args)方法

  • 这个方法会使用一个 CallAdapter 对象来把 OkHttpCall 对象进行转换,生成一个新 的对象。默认情况下,返回的是一个 ExecutorCallbackCall ,它的作用是把操 作切回主线程后再交给 Callback 。
  • 另外,如果有自定义的 CallAdapter,这里也可以生成别的类型的对象,例如 RxJava 的 Observable ,来让 Retrofit 可以和 RxJava 结合使用。
callAdapter是哪里来的呢?
  • 在retrofit进行build()的时候会创建一个默认的callAdapter,放在callAdapterFactories容器中
  • 用户自定义的callAdapter需要在retrofit对象构建时传入
adapt

最后流程总结如下

我的总结

一句话总结
Retrofit通过动态代理为接口创建了实例进行了okhttp网络请求.
其内部通过拦截接口参数、注解解析等方式获取到了http网络请求所需的各种信息,okhttp请求结果通过数据转换和线程调度返回给调用者。

小结

  1. Retrofit 通过动态代理为接口创建了实例进行okhttp网络请求
  2. Retrofit 核心方法是loadServiceMethod(method).invoke(args)
  3. loadServiceMethod(method).invoke(args) 这其中又包含了loadServiceMethod(method)和invoke(args)两个部分

loadServiceMethod(method)

  • loadServiceMethod(method)方法调用了ServiceMethod.parseAnnotations(...)并返回一个RequestFactory对象
  • 这个方法中首先调用了RequestFactory.parseAnnotations(retrofit, method)方法,这个方法实际上通过RequestFactory的build方法构建了一个RequestFactory对象,这个对象中包含了http请求所需的信息,具体是通过解析注解信息(包括返回值类型、方法注解、参 数类型、参数注解)

然后通过返回的RequestFactory对象构造了一个CallAdapted对象

//参数1,包含请求信息的requestFactory
//参数2,实际上就是OkHttpClient
//参数3,数据转换器
//参数4,包装了Android默认MainThreadExecutor的DefaultCallAdapterFactory对象 或RxJava2CallAdapterFactory等
CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);

invoke(args)
invoke方法创建了OkHttpCall对象,在里边是通过okhttp进行网络请求

  • adapt方法:这个方法会使用一个 CallAdapter 对象来把 OkHttpCall 对象进行转换,生成一个新 的对象。默认情况下,返回的是一个 ExecutorCallbackCall ,它的作用是把操 作切回主线程后再交给 Callback 。
  • 另外,如果有自定义的 CallAdapter,这里也可以生成别的类型的对象,例如 RxJava 的 Observable ,来让 Retrofit 可以和 RxJava 结合使用。

Retrofit中的注解

请求方法类
retrofit注解1
参数类
retrofit注解2
标记类
retrofit注解2

Retrofit中的设计模式

1.建造者模式

Retrofit对象的创建、ServiceMethod对象创建都使用Build模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用Build的相关方法进行配置创建对象。
如Retrofit.builder等

2.外观模式

Retrofit对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。
门面模式:提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使用子系统更容易使用

3.代理模式

Retrofit核心代码Proxy.newProxyInstance() 动态代理
好处:通过代理帮我们进行具体的接口实现,而不需要我们写具体的接口实现 retrofit代理模式

4. 其他:适配器模式、工厂模式、策略模式、单例模式

适配器模式、工厂模式

retrofit2.CallAdapter类
适配器模式

public interface CallAdapter<R, T> {
    ...
    T adapt(Call<R> call);
    ...
}

工厂模式: 返回不同的adapter

  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);
    }
  }
策略模式

retrofit2.Converter类就使用了策略模式

单例模式(饿汉式)

retrofit2.Platform类

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

  static Platform get() {
    return PLATFORM;
  }
  ...
}

Retrofit面试题解

  • retrofit线程切换问题
    • MainThreadExecutor中的Handler handler = new Handler(Looper.getMainLooper());实际上就是通过handler调度
  • rxjava和retrofit如何结合进行网络请求
    • 通过addCallAdapterFactory(RxJavaCallAdapterFactory.create())添加rxjava适配器
    • 网络请求接口创建的是Observable对象
    • 通过rxjava操作符进行线程调度
  • Hook与动态代理
    • Hook:通过某种手段对一件事物进行改头换面,从而劫持Hook的目标来以达到控制目标的行为的目的。

参考附录

Retrofit源码解析
Retrofit最详细的源码解析