美文网首页
Retrofit 与 OkHttp 流程梳理

Retrofit 与 OkHttp 流程梳理

作者: 弄码哥nomag | 来源:发表于2020-04-04 10:44 被阅读0次

    Retrofit 与 OkHttp 流程梳理

    前言:

    本文会带你分析一下,retrofit 是怎么一个 java 的 interface 转化成了一个 okhttp 的请求,并把请求的 model 返回给业务层的。

    注解定义的参数在哪里解析:

    首先我们看一下,我们对于 java 的 interface 添加的注解是怎么解析的。Retrofit 整体采用了一个 Builder 模式来创建新的对象。在这里面,会设置一些相关的工厂。主要有四类需要关注的:

    1. okhttp3.Call.Factory callFactory = this.callFactory;
    2. Executor callbackExecutor = this.callbackExecutor;
    3. List<CallAdapter.Factory> callAdapterFactories
    4. List<Converter.Factory>
      这里会指定 okhttp3.Call.Factory ,这也是后面我们创建 ServiceMethod 的实例的时候,创建实际创建 okhttp call 的时候使用的工厂。

    之后,我们进入正题:Proxy.newProxyInstance
    这里是利用了 java 的动态代理机制,将所有对代理对象的请求都转发给 InvocationHandler 中。我们只需要实现 InvocationHandler 就可以。一般时候我们使用动态代理,是我们有一个现成的对象,比如叫 MyInterfaceImpl myInterfaceImpl,那么,如果我们想使用动态代理的话,就可以在 InvocationHandler 中拿到对目标方法的请求,然后,常见的是在请求的前后打 log,然后实际的去调用请求,比如:method.invoke(myInterfaceImpl, args) 。在 retrofit 中,我们拿到了请求的目标 method 后,必须得明确,我们拿到的其实并不是一个实际的可以运行的方法,因为我们并没有一个类似 MyInterfaceImpl 的接口的实现类。这也就引出了 retrofit 跟我们平时写的一般的动态代理的不同的点:一般的动态代理只是在目标对象的方法调用前后,进行拦截;而 retrofit 是通过动态代理来动态生成目标对象的目标方法。也就是说,retrofit 在运行的时候,只有一个代理类,而没有这个接口的真正的实现类。而这个代理类的用处,只是得知我们的代码的“意图”。

    回想我们使用 retrofit 时候,会定义一个接口,然后对里面的接口中的方法添加相关的注解。正是这些注解,提高了 retrofit 的解耦性,堪称是 retrofit 的灵魂!那么这些注解具体是怎么解析的呢?

    在 InvocationHandler 的 invoke 中,会通过调用 loadServiceMethod(method).invoke(args) 来创建真正的 ServiceMethod,并把方法的调用代理给他。这个 ServiceMethod 对象的作用就跟我们上文中说的接口的实现类 myInterfaceImpl 的作用是一样的。loadServiceMethod中,会先检查一个缓存map,如果有 ServiceMethod 对象,就直接返回;如果没有,就新建。毕竟 ServiceMethod 对象的创建涉及注解的解析,实际运行的时候还是比较重的,非常适合对 ServiceMethod 实例对象进行缓存。

    loadServiceMethodServiceMethod.parseAnnotations(this, method) 中,又把整个函数的流指向了 HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory),而这里正是真正实现了解析注解并生成 ServiceMethod 实例的过程。可以看到,里面首先创建了 CallAdapter,并解析出了 responseType

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

    然后创建了 responseConverter

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

    然后会创建出 CallAdapted 实例。这里的 CallAdapted实例,是继承自 HttpServiceMethod 的,也就是运行时真正的实例。

    然后,回到我们在 InvocationHandler 中看到的 loadServiceMethod(method).invoke(args),这里就会调用 HttpServiceMethod中的 invoke 方法。

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

    这里会真正的创建 OkHttpCall,然后把这个 call 通过 callAdapter 转化一下。具体的转化的目的和流程,我们会在后面提到。

    请求的 enqueue 和 execute 怎么最终调用到 OkHttp 的?

    上面我们了解到,ServiceMethod 最终会创建一个 OkHttpCall 作为请求的真正执行者。虽然在创建出 OkHttpCall 实例之后,还会调用 adapt 来把这个 Call 转化一下,然后提供给业务层使用,但是,转换之后的,比如默认的 ExecutorCallbackCall,其实是一个静态代理, 还是会把请求真正执行的部分代理给 OkHttpCall

    我们知道,在使用 retrofit 的时候,有两种方式:executeenqueue。前者是同步方法,后者是异步方法。简单起见,我们只分析同步方法。

    @Override public Response<T> execute() throws IOException {
      okhttp3.Call call;
    
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;
    
    // ...
    
        call = rawCall;
        if (call == null) {
          try {
            call = rawCall = createRawCall();
          } catch (IOException | RuntimeException | Error e) {
            throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
            creationFailure = e;
            throw e;
          }
        }
      }
    
      if (canceled) {
        call.cancel();
      }
    
      return parseResponse(call.execute());
    }
    

    首先会判断 executed 变量。一个请求只能执行一次,再次执行的时候就会报错。然后是通过 createRawCall()来创建真正的 okhttp3.Call 。然后是在执行之前,会检查是否 cancel。然后会调用 call.execute()获取请求结果。最后是 parseResponse 来对请求结果进行解析。

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
      ResponseBody rawBody = rawResponse.body();
    
      // Remove the body's source (the only stateful object) so we can pass the response along.
      rawResponse = rawResponse.newBuilder()
          .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
          .build();
    
      int code = rawResponse.code();
      if (code < 200 || code >= 300) {
        try {
          // Buffer the entire body to avoid future I/O.
          ResponseBody bufferedBody = Utils.buffer(rawBody);
          return Response.error(bufferedBody, rawResponse);
        } finally {
          rawBody.close();
        }
      }
    
      if (code == 204 || code == 205) {
        rawBody.close();
        return Response.success(null, rawResponse);
      }
    
      ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
      try {
        T body = responseConverter.convert(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;
      }
    }
    

    首先是处理 if (code < 200 || code >= 300) {,也就是请求出错的情况。然后检查是否是 204 或者 205。这两种情况的大多数是为了表示“请求成功了,但是没有数据返回给客户端”,这样做的好处是可以减少不必要的数据传输,只通过一个状态码就表达了状态。

    然后就是正常的请求处理过程,也就是调用 responseConverter.convert()。比如,我们最常用的 GsonConverter 里面的实现就是:

    @Override public T convert(ResponseBody value) throws IOException {
      JsonReader jsonReader = gson.newJsonReader(value.charStream());
      try {
        T result = adapter.read(jsonReader);
        if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
          throw new JsonIOException("JSON document was not fully consumed.");
        }
        return result;
      } finally {
        value.close();
      }
    }
    

    会通过 jsonReader 把返回的 body 转化成 json,然后通过 TypeAdapterjson 转化成上层业务方使用的 model

    关于 CallAdapter

    return new CallAdapter<Object, Call<?>>() {
        public Type responseType() {
            return responseType;
        }
    
        public Call<Object> adapt(Call<Object> call) {
            return (Call)(new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
        }
    };
    

    可以看到,retrofit默认的 callAdapter 是把一个 OkHttpCall,转化成了一个 ExecutorCallbackCall 。相当于对 OkHttpCall 做了一个静态代理:ExecutorCallbackCall里面的 enqueueexecute 方法都是直接代理给 OkHttpCall 的实例来执行的。然后,观察 ExecutorCallbackCall 的 enqueque 方法,会发现,在 delegateonResponse 里面,会调用

    ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
        public void run() {
            if (ExecutorCallbackCall.this.delegate.isCanceled()) {
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
            } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
            }
        }
    });
    

    人如其名。也就是,ExecutorCallbackCall会简单的在 onResponse 里面,把结果放在 executor 里面执行。在android中,就是一个 MainThreadExecutor

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());
    
      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
    

    而,对于 retrofit 的两种使用方式:executeenqueueExecutorCallbackCall也只是处理了 enqueue,对异步的请求结果通过线程转换的方式让业务层直接在主线程拿到相应的 model。对于 execute 的同步请求,直接代理给了底层的 OkHttpCall

    相关文章

      网友评论

          本文标题:Retrofit 与 OkHttp 流程梳理

          本文链接:https://www.haomeiwen.com/subject/uxpdphtx.html