Retrofit框架简单分析

1,327 阅读4分钟

之前使用Retrofit来承接网络请求,根据官网Demo快速上手,使用起来也很方便,特别是使用注解来设置请求参数,比之前手写接口省了不少事。但是用着很爽,总有一个疑问在心头,Retrofit框架是怎么解析注解参数的?又是怎么使用OkHttp发送请求的?现在带着疑问,通过阅读源码来慢慢了解下Retrofit框架内部是如何工作的。

创建Retrofit

使用内置的Builder方便快速的组装出一个Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

这里使用了构建者模式来创建对象,那么什么是构建者模式呢?

The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations. 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示

简单的理解一下,使用链式调用的方式,一步步设置参数,并组装出一个复杂的对象。

参考链接:zhuanlan.zhihu.com/p/58093669

定义接口,并创建对应的Service

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

GitHubService service = retrofit.create(GitHubService.class);

我们只定义了一个接口GitHubService,那么create方法如何创建实现类的呢?进入create方法里边看看都做了什么操作。

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

          @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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

可见这里使用了动态代理模式来创建接口的实现类,想要理解create,必须先理解什么是动态代理,详细了解可以参考这里

结合GitHubService来看,Proxy.newProxyInstance创建了GitHubService的实现类,并强转为GitHubService类型。

有了service,我们就可以调用接口了 :

Call<List<Repo>> repos = service.listRepos("octocat");

这其实是通过动态代理生成的代理类来调用接口,此时会触发InvocationHandler的invoke方法,进而会走到loadServiceMethod的地方。

先了解下InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

简单的理解就是代理实例的方法被调用时,其方法调用会被派发到InvocationHandler的invoke方法。

继续往下看源码

loadServiceMethod

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对象,并且是有缓存机制的,如果已经加载过了,就直接从缓存中获取。这里发现调用了parseAnnotations方法,顾名思义,可见在这里对注解进行了解析,进去看看详情。

ServiceMethod

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

到这里发现,注解由RequestFactory来解析,获取请求的url,请求方式,请求参数等一系列信息。最后HttpServiceMethod.parseAnnotations返回一个ServiceMethod对象,其实是HttpServiceMethod的三个子类之一,

CallAdapted、SuspendForBody 或 SuspendForResponse,具体使用哪个和具体的接口定义有关,本文的接口定义 Call<List<Repo>> listRepos(@Path("user") String user);会返回CallAdapted,如果接口定义使用Kotlin的挂起函数,则会返回后两者之一。

invoke

根据上文分析,可以知道动态代理中loadServiceMethod调用的invoke方法,其实就是HttpServiceMethod的invoke方法:

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

这里创建了一个OkHttpCall对象,这是对OkHttp的封装类,请求最终是通过OkHttpCall调用OkHttp的方法发出的,可查看OkHttpCall的源码了解详情。OkHttpCall创建时传入了一个responseConverter,这个就是负责将请求结果转换为我们定义的对象的转换器,即通过在创建Retrofit对象时设置的ConverterFactory获取,.addConverterFactory(GsonConverterFactory.create())

adapt

invoke方法调用了adapt方法,本文例子其实就是调用了CallAdapted的adapt方法。

@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  return callAdapter.adapt(call);
}

这个adapt方法调用了callAdapter的adapt方法,继续跟踪,callAdapter如果没有明确设置,会有默认设置,DefaultCallAdapterFactory可以获取一个CallAdapter对象,看看DefaultCallAdapterFactory的get方法:

@Override public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  ……
  
  final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;
        
  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

adapt方法最终返回一个Call对象可见如果接口设置了SkipCallbackExecutor注解,则直接返回OkHttpCall,没有设置,返回ExecutorCallbackCall。

ExecutorCallbackCall

static final class ExecutorCallbackCall<T> implements Call<T> {
  final Executor callbackExecutor;
  final Call<T> delegate;

  ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
  }

  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    delegate.enqueue(new Callback<T>() {
      @Override public void onResponse(Call<T> call, final Response<T> response) {
        callbackExecutor.execute(new Runnable() {
          @Override public void run() {
            if (delegate.isCanceled()) {
              // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
              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(new Runnable() {
          @Override public void run() {
            callback.onFailure(ExecutorCallbackCall.this, t);
          }
        });
      }
    });
  }
	……
}

这里真正看到了OKHttp发送请求的enqueue方法,ExecutorCallbackCall的一个主要作用是将请求结果回调到主线程,具体使用的是MainThreadExecutor,这个类很简单,就是用一个持有主线程Looper的Handler来处理。

static class MainThreadExecutor implements Executor {
  private final Handler handler = new Handler(Looper.getMainLooper());

  @Override public void execute(Runnable r) {
    handler.post(r);
  }
}

总结

至此,Retrofit框架解析注解,并通过OkHttp发送请求的流程基本分析完毕,总结下最开始的疑问:

  • 怎么解析的注解?

    主要通过RequestFactory来解析注解,获取请求的各种参数。

  • 怎么使用OkHttp发送请求的?

    使用OkHttpCall来最终调用OkHttp的方法来发送请求

参考:juejin.cn/post/684490…