Retrofit原理解析

retrofit和okhttp都是用来做网络请求的,并且都是出自Square公司。其中okhttp是用来代替android原生的HttpClient和HttpURLConnection的,官方也是建议用okhttp作为android底层的网络通信库。而retrofit将okhttp进一步封装,让开发者更方便的做网络请求。

通过本章的学习我们能够了解并掌握:

  1. retrofit整体框架
  2. retrofit是如何封装okhttp来做请求的?
  3. retrofit如何做数据解析的?

retrofit整体框架

先根据官网的教程,写一个简单的例子:

//1.申明接口
interface GitHubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String?): Call<List<Repo>>

    fun listRepos2(@Path("user") user: String?): Single<List<Repo>>
}

//2.创建retrofit
val retrofit = Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .baseUrl("https://api.github.com/")
    .build()

//3.实现接口
val service = retrofit.create(GitHubService::class.java)

//4.调用接口
val repos: Call<List<Repo>> = service.listRepos("longdw")

//5.发起请求
repos.enqueue(object: Callback<List<Repo>> {
    override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
        println("response-->${response.body()!![0].name}")
    }

    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
    }
})

只需要简单的5个步骤,就能够实现请求网络并将服务器返回的数据解析成对象,并且能直接在onResponse()onFailure()回调方法里面直接操作UI。

是不是简单方便又高效,接下来开始深入源码内部。当我尝试进入enqueue()方法或者listRepos()方法查看具体调用过程时,发现都是接口,找不到具体的实现类,也就是说第4、5步走不下去了,这可咋办呢?那就从第3步开始看,从retrofit.create()寻找突破口:

public final class Retrofit {

    ...

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

      ...

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

上面选取部分Retrofit源码,其中create()方法是Retrofit的核心,该方法有两个关键点:

(1)validateServiceInterface(service)

该方法我没贴出来,主要作用是验证我们写的接口有没有问题,比如接口不能有泛型。大家看源码后会发现该方法里面有个validateEagerly关键字,默认是false,如果设置为true的话,会在接口中的方法还没调用的时候,也就是只在我们调用retrofit.create()的时候就开始检查所有方法是否合法了。所以我们在生产环境不要开启这个,否则可能会导致应用启动速度慢,如果接口中有很多方法,还有可能会导致ANR等问题。

(2)Proxy.newProxyInstance()

这个就是我们常看到或者听到的动态代理,有3个参数:ClassLoader、Class数组、InvocationHandler匿名类。其中就Retrofit来讲,第二个参数传的是GitHubService,其基本原理就是当我们调用此接口中的方法时,比如上例中的listRepos()方法,动态代理会最终调用InvocationHandler匿名类中的invoke(Object proxy, Method method, @Nullable Object[] args)方法。用伪代码表示就是长下面这样:

public class GitHubServiceImpl 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 the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }
            args = args != null ? args : emptyArgs;
            return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
        }
    };

    @NonNull
    @Override
    public Call<List<Repo>> listRepos(@Nullable String user) {
        Method method = GitHubService.class.getMethod("listRepos", String.class);
        return invocationHandler.invoke(this, method, user);
    }
}

通过上面的介绍,我们了解到Retrofit的核心实际上就是运用了动态代理作为桥梁,当我们调用接口方法时,动态代理会自动创建一个类作为代理类,用代理类来包装我们的请求。

retrofit是如何封装okhttp来做请求的?

本小结重点来讲解下代理类中都干了些啥。

InvocationHandler.invoke()作为切入点,首先判断如果调用的方法是Object中的方法,比如toString()hashCode()等,就直接调用,否则调用loadServiceMethod(method).invoke(args)

loadServiceMethod(method)里面,通过ServiceMethod.parseAnnotations()解析出接口方法的注解传入的参数返回的参数等信息,最终以HttpServiceMethod类型返回,并用serviceMethodCache缓存起来。通过调用HttpServiceMethodinvoke()方法来包装请求的过程以及数据解析的过程。

接下来以loadServiceMethod(method).invoke(args)invoke()方法作为切入点,先看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);
}

再看HttpServiceMethod类。

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
        Retrofit retrofit, Method method, RequestFactory requestFactory) {

        CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
        
        ...

        Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);

        if (!isKotlinSuspendFunction) {
            return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
        } else ...

    }

    private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
        Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
        try {
        //noinspection unchecked
        return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
        } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(method, e, "Unable to create call adapter for %s", returnType);
        }
  }

  private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
    Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    try {
      return retrofit.responseBodyConverter(responseType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create converter for %s", responseType);
    }
  }

  HttpServiceMethod(
      RequestFactory requestFactory,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, ResponseT> responseConverter) {
    this.requestFactory = requestFactory;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }

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

  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

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

以上是HttpServiceMethod的核心代码,loadServiceMethod(method).invoke(args)最终调用的是HttpServiceMethodinvoke()方法,主要做了两件事:

(1)创建OkHttpCall对象

final class OkHttpCall<T> implements Call<T> {

  OkHttpCall(
      RequestFactory requestFactory,
      Object[] args,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, T> responseConverter) {
    this.requestFactory = requestFactory;
    this.args = args;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }

  private okhttp3.Call getRawCall() {
    okhttp3.Call call = rawCall;
    return rawCall = createRawCall();
  }

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    return call;
  }

  @Override
  public void enqueue(final Callback<T> callback) {

    okhttp3.Call call;

    call = rawCall = createRawCall();

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            response = parseResponse(rawResponse);

            callback.onResponse(OkHttpCall.this, response);
          }

          @Override
          public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }

          private void callFailure(Throwable e) {
            callback.onFailure(OkHttpCall.this, e);
          }
        });
  }

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    T body = responseConverter.convert(catchingBody);
    return Response.success(body, rawResponse);
  }
}

OkHttpCall类里面同样也有几个需要关注的重点

a)enqueue()方法,Call通过此方法来发起调用。

b)通过callFactory.newCall()来创建真正的发起网络请求的Call

c)通过responseConverter.convert()来解析服务器返回的数据。

这几个方法等会再详细介绍,先继续往下看。

(2)调用adapt()方法

实际上调用的是CallAdapted.adapt()方法,接着执行callAdapter.adapt(call)。看上面HttpServiceMethod源码我们得知,callAdapter实际上是retrofit.callAdapter()创建的。接着我们再看Retrofit源码:

public final class Retrofit {

    Retrofit(
            okhttp3.Call.Factory callFactory,
            HttpUrl baseUrl,
            List<Converter.Factory> converterFactories,
            List<CallAdapter.Factory> callAdapterFactories,
            @Nullable Executor callbackExecutor,
            boolean validateEagerly) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
        this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
        this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
        this.callbackExecutor = callbackExecutor;
        this.validateEagerly = validateEagerly;
    }

    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
        return nextCallAdapter(null, returnType, annotations);
    }

    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
        int start = callAdapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
            CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
            if (adapter != null) {
                return adapter;
            }
        }
    }

    public static final class Builder {
        public Retrofit build() {
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }

            okhttp3.Call.Factory callFactory = this.callFactory;
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }

            Executor callbackExecutor = this.callbackExecutor;
            if (callbackExecutor == null) {
                callbackExecutor = platform.defaultCallbackExecutor();
            }

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

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

            // Add the built-in converter factory first. This prevents overriding its behavior but also
            // ensures correct behavior when using converters that consume all types.
            converterFactories.add(new BuiltInConverters());
            converterFactories.addAll(this.converterFactories);
            converterFactories.addAll(platform.defaultConverterFactories());

            return new Retrofit(
                    callFactory,
                    baseUrl,
                    unmodifiableList(converterFactories),
                    unmodifiableList(callAdapterFactories),
                    callbackExecutor,
                    validateEagerly);
        }
    }
}

看到这里我们得停下来,思考下我们的初衷是什么,我们的目的是要找到callAdapter是如何创建的,进而找到adapt()方法的逻辑。

仔细阅读上面的源码会发现,callAdapter是由Builderbuild()方法创建的callAdapterFactories创建的。callAdapterFactories是个集合,默认的callAdapter是由platform.defaultCallAdapterFactories(callbackExecutor)创建。callbackExecutor参数是一个Executor

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

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

defaultCallAdapterFactories()返回的是DefaultCallAdapterFactory

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

    final Executor executor = 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);
      }
    };
  }

  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) {
      Objects.requireNonNull(callback, "callback == null");

      delegate.enqueue(
          new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, final Response<T> response) {
              callbackExecutor.execute(
                  () -> {
                    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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
            }
          });
    }

    @Override
    public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override
    public Response<T> execute() throws IOException {
      return delegate.execute();
    }
  }
}

看到这里大家应该长叹一口气,我们要的CallAdapter终于找到了——就是DefaultCallAdapterFactory.get()返回的对象。还记得上面callAdapter.adapt(call)吗?我们当初就是从这里一步步往下深入源码的。

接着再看adapt(call),返回的是ExecutorCallbackCall。原来饶了这么一圈,又给我包装了一个ExecutorCallbackCall。回到开头的例子中,当我们调用call.enqueue()时,实际上调用的是这个包装类ExecutorCallbackCall里面的enqueue(),然后该方法内部又调用了间接发起网络请求的OkHttpCall类中的enqueue(),最后在OkHttpCall类的enqueue()方法内部调用直接发起网络请求的okhttp3.Call类的enqueue()

有人会疑惑,为啥一个调用要包装两层呢?

首先说下ExecutorCallbackCall,这个类大家注意看下onResponse()onFailure()这两个回调,不是直接调用callback.xxx(),而是包装了一层callbackExecutor.execute(),这个callbackExecutor就是我们上面贴出来的MainThreadExecutor,是不是瞬间明白了?没错,就是将回调信息通过主线程返回。

再说下OkHttpCall,用这个类又包装了下,它的作用是方便在内部处理返回的信息,比如调用我们传入的responseConveter来解析数据。

看到这里,相信大家对Retrofit整体的流程应该有了一个清晰的认识了,最后我们再看看前面留下的两个没有深入讲解的知识点:

1)通过callFactory.newCall()创建okhttp3.Call。2)通过responseConverter.convert()来解析数据。

retrofit如何做数据解析的?

首先讲下callFactory.newCall()是怎么创建okhttp3.Call的。

callFactory其实跟callAdapter的套路差不多,结合HttpServiceMethodRetrofit源码,找到Builderbuild()方法中的callFactory,实际上就是OkHttpClient()

最后再讲下responseConverter.convert()是怎么解析数据的。

同样的套路,找到Builderbuild()中的converterFactories集合。还记得我们初始化Retrofit传入的addConverterFactory(GsonConverterFactory.create())吗?没错,最终是通过GsonConverterFactory来解析数据的。

至此,整个Retrofit的原理,包括调用过程以及数据的解析过程都讲解完毕了,当然细节部分比如怎么解析注解和参数等,这些对照源码看下应该都能明白。本篇文章主要还是讲解整个大的框架,先从宏观上把握了整个流程,再去深入源码,就能够掌握更多的细节。不能一开始就抠细节,这样到最后自己都搞晕了,就更没信心继续往下看了。

转载请注明出处——https://www.longdw.com