中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

OkHttp與Retrofit的區別有哪些

發布時間:2021-12-03 13:50:23 來源:億速云 閱讀:193 作者:小新 欄目:移動開發

這篇文章主要為大家展示了“OkHttp與Retrofit的區別有哪些”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“OkHttp與Retrofit的區別有哪些”這篇文章吧。

參考答案:
OkHttp和Retrofit都是目前流行網絡開源框架

封裝不同:
Retrofit封裝了具體的請求,線程切換以及數據轉換。
retrofit通過使用代理,外觀,策略模式對okhttp進行了封裝
OkHttp 是基于Http協議封裝的一套請求客戶端

職責不同:
Retrofit主要負責應用層面的封裝,面向開發者,方便使用,比如請求參數,響應數據的處理,錯誤處理等等。
OkHttp主要負責socket部分的優化與封裝,比如網絡訪問,多路復用,buffer緩存,數據壓縮等等。
OkHttp與Retrofit的區別有哪些

順手留下GitHub鏈接,需要獲取相關面試等內容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

Retrofit 可以說和 OkHttp 是親兄弟了,它們都是由 Square 公司推出的網絡請求庫,并且 Retrofit 實際上是基于 OkHttp 實現的,它在 OkHttp 現有功能的基礎上進行了封裝,支持通過注解進行網絡請求參數的配置,同時對數據返回后的解析、序列化進行了統一的包裝,甚至在近期引入了對協程對支持。

今天就讓我們一起來看看 Retrofit 是如何在 OkHttp 這樣一個已經固定的框架的基礎上,優雅的進行封裝并拓展功能的。

基本使用
我們首先來看看 Retrofit 的基本使用,來對它有個大致的了解。

首先,我們可以構建一個如下的請求 Service 類,它里面對各個請求的方法及參數通過注解進行了標注:

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

之后,我們可以構建一個 Retrofit 對象,并通過 Retrofit.create 方法傳入對應 class 從而構建對應的 Service 對象:

  Retrofit retrofit = new Retrofit.Builder()
     baseUrl("https://api.github.com/")
     build();
  }
  GitHubService service = retrofit.create(GitHubService.class);

之后,我們調用 service 中對應的方法,就可以獲取到 Call 對象了。

通過對 Call 對象調用 enqueue 就可以實現對請求的異步調用,而通過 execute 方法則可以實現請求的同步調用。

Retrofit 對象的構建

Retrofit 采用了 Builder 模式進行了構建,在 Builder 中可以進行非常多的配置,其中可以對 baseUrlokhttpClientconverterFactorycallAdapterFactory 等進行設置。

這里沒什么特別的,都是一些簡單的賦值,就不再關注了,我們只需要看看最后 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;
  }
Service 對象的創建
動態代理創建 Service 代理類

接著我們看到自己定義的 interface 是如何僅僅靠傳遞 classRetrofit.create 就能實現實例的獲取的,它明明只是個接口呀?

  public <T> T create(final Class<T> service) {
     // 對 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 {
            // Object 類的方法照常調用
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // 如果是對應平臺本身的類就有的方法,照常調用
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 否則通過 loadServiceMethod 方法獲取到對應 Method 并 invoke
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
            }
         });
  }

可以看到,實際上 Service 對象的獲取是通過動態代理實現的。這里首先通過 validateServiceInterface 方法對接口進行了檢測,之后通過動態代理對該接口進行了代理。

對于 Object 類本身獨有以及對應平臺本身就存在的方法,就照常調用,否則通過 loadServiceMethodService 中對應的 Method 對象進行處理,之后對其調用 invoke 方法。

這里說明了 Retrofit 不是在創建 Service 接口對應對象時就立即對所有該接口中的所有方法都進行注解的解析,而是采用了在方法被調用時才進行注解的解析這種懶加載的思想。

接著我們看看 validateServiceInterface 方法:

  private void validateServiceInterface(Class<?> service) {
      // 判斷是否是接口
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    // 判斷該接口及其繼承的所有接口是否包含了范型參數,如果包含則拋出異常
    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
    while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
      if (candidate.getTypeParameters().length != 0) {
        StringBuilder message = new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
        if (candidate != service) {
          message.append(" which is an interface of ").append(service.getName());
        }
        throw new IllegalArgumentException(message.toString());
      }
      Collections.addAll(check, candidate.getInterfaces());
   }
   // 如果在創建Retrofit時設置了很急切地對Service的方法進行處理,則對非平臺獨有且非static的方法通過 loadServiceMethod 方法進行處理。
   if (validateEagerly) {
     Platform platform = Platform.get();
     for (Method method : service.getDeclaredMethods()) {
       if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
         loadServiceMethod(method);
       }
     }
   }
  }

首先,這個方法對 service 進行了檢測,保證了它是一個接口并且它和它繼承的類中沒有范型參數。

之后如果在 Retrofit 創建時設置 validateEagerly 為 true 的話,會對 Service 中所有非平臺獨有且非static的方法通過 loadServiceMethod 方法提前進行處理

Service 中方法的解析
那么我們來看看 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;
  }

首先它會采用 Double Check 的方式嘗試從 serviceMethodCache 緩存中獲取 ServiceMethod 對象,如果獲取不到則通過 ServiceMethod.parseAnnotations 方法對該 Method 的注解進行處理并將得到的 ServiceMethod 對象加入了緩存。

也就是說為了避免多次對方法的注解進行處理,Retrofit 采用了一個 serviceMethodCache 對解析后的 ServiceMethod 進行緩存。

接著我們就來看看,parseAnnotations 方法是如何對方法的注解進行解析的。

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

這里先通過 RequestFactory.parseAnnotations 方法對注解解析并獲得了一個 RequestFactory 對象。

之后又通過 HttpServiceMethod.parseAnnotations 方法傳入了 requestFactory繼續進行注解的解析并獲得 ServiceMethod 對象

注解解析

我們先看看 RequestFactory.parseAnnotations

  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
  }

它把 Method 傳入 Builder 從而構建了一個新的 RequestFactory

  Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    this.methodAnnotations = method.getAnnotations();
    this.parameterTypes = method.getGenericParameterTypes();
    this.parameterAnnotationsArray = method.getParameterAnnotations();
  }

Builder 中通過反射獲取到method所包含的注解、參數包含的范型以及參數的注解。

接著看看 build 方法:

  RequestFactory build() {
    for (Annotation annotation : methodAnnotations) {
        // 遍歷方法注解對每個注解進行解析
      parseMethodAnnotation(annotation);
    }
    // ...異常的處理

   // 對參數進行解析
    int parameterCount = parameterAnnotationsArray.length;
    parameterHandlers = new ParameterHandler<?>[parameterCount];
    for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
      parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
    }

    // ... 異常的處理
   return new RequestFactory(this);
  }

在 build 方法中主要是對方法的每個注解調用了 parseMethodAnnotation 進行了解析,并且對每個參數調用了 parseParamter 方法解析為了 ParamterHandler 對象。

parseMethodAnnotation 的代碼如下:

  private void parseMethodAnnotation(Annotation annotation) {
    if (annotation instanceof DELETE) {
      parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
    } else if (annotation instanceof GET) {
      parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
    } else if (annotation instanceof HEAD) {
      parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
    } else if (annotation instanceof PATCH) {
      parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
    } else if (annotation instanceof POST) {
      parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
    } else if (annotation instanceof PUT) {
      parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
    } else if (annotation instanceof OPTIONS) {
      parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
    } else if (annotation instanceof HTTP) {
      HTTP http = (HTTP) annotation;
      parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
    } else if (annotation instanceof retrofit2.http.Headers) {
      String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
      if (headersToParse.length == 0) {
        throw methodError(method, "@Headers annotation is empty.");
      }
      headers = parseHeaders(headersToParse);
    } else if (annotation instanceof Multipart) {
      if (isFormEncoded) {
        throw methodError(method, "Only one encoding annotation is allowed.");
     }
     isMultipart = true;
    } else if (annotation instanceof FormUrlEncoded) {
      if (isMultipart) {
        throw methodError(method, "Only one encoding annotation is allowed.");
     }
     isFormEncoded = true;
   }
  }

這里實際上就是對每一種 HTTP 所支持的類型進行了支持,獲取到了對應注解的中的 url,并調用parseHttpMethodAndPath 進行處理,同時對 Headers 注解則是通過 parseHeaders 進行了處理。

對 Http 請求方式和 Path 的處理

對于 Method 和 Path,通過 parseHttpMethodAndPath 進行了參數的賦值:

  private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    if (this.httpMethod != null) {
      throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.",
          this.httpMethod, httpMethod);
    }
    this.httpMethod = httpMethod;
    this.hasBody = hasBody;
    if (value.isEmpty()) {
      return;
    }
    // Get the relative URL path and existing query string, if present.
    int question = value.indexOf('?');
    if (question != -1 && question < value.length() - 1) {
      // Ensure the query string does not have any named parameters.
      String queryParams = value.substring(question + 1);
      Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
      if (queryParamMatcher.find()) {
        throw methodError(method, "URL query string \"%s\" must not have replace block. "+ "For dynamic query parameters use @Query.", queryParams);
      }
    }
    this.relativeUrl = value;
    this.relativeUrlParamNames = parsePathParameters(value);
  }

這里實際上就是對不同 HTTP 請求方式和 Path 進行了賦值,同時通過正則表達式保證了這個接口的 Path 中沒有包含參數。

對 Headers 的處理
  private Headers parseHeaders(String[] headers) {
    Headers.Builder builder = new Headers.Builder();
    for (String header : headers) {
      int colon = header.indexOf(':');
      if (colon == -1 || colon == 0 || colon == header.length() - 1) {
        throw methodError(method,
            "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
      }
      String headerName = header.substring(0, colon);
      String headerValue = header.substring(colon + 1).trim();
      if ("Content-Type".equalsIgnoreCase(headerName)) {
        try {
          contentType = MediaType.get(headerValue);
        } catch (IllegalArgumentException e) {
          throw methodError(method, e, "Malformed content type: %s", headerValue);
        }
      } else {
        builder.add(headerName, headerValue);
      }
    }
    return builder.build();
  }

而對于 Headers 則是將傳遞進來的 Headers 列表解析為了對應的 Headers 對象。

對方法參數的處理
最后我們看看對方法參數的處理:
  private @Nullable ParameterHandler<?> parseParameter(
      int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
    ParameterHandler<?> result = null;
    if (annotations != null) {
      for (Annotation annotation : annotations) {
          // 對每個注解通過 parseParameterAnnotation 進行解析
        ParameterHandler<?> annotationAction =
            parseParameterAnnotation(p, parameterType, annotations, annotation);
        if (annotationAction == null) {
          continue;
        }
        if (result != null) {
          throw parameterError(method, p,
              "Multiple Retrofit annotations found, only one allowed.");
        }
        result = annotationAction;
      }
    }
    if (result == null) {
        // 在協程的情況下對進行特殊處理
      if (allowContinuation) {
        try {
          if (Utils.getRawType(parameterType) == Continuation.class) {
            isKotlinSuspendFunction = true;
            return null;
          }
        } catch (NoClassDefFoundError ignored) {
        }
      }
      throw parameterError(method, p, "No Retrofit annotation found.");
    }
    return result;
  }

parseParamterAnnotation 方法的代碼太長了,這里就不再貼了,它對方法的每個注解都進行了獨有的處理,并返回了對應的 ParamterHandler

可以發現,RequestFactory.parseAnnotations 的主要作用就是完成對方法注解信息的解析,從而用于產生對應的 Request

ServiceMethod 的創建
之后我們接著看看 HttpServiceMethod.parseAnnotations
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
        // 如果方法是 kotlin 中的 suspend 方法
      Type[] parameterTypes = method.getGenericParameterTypes();
      // 獲取 Continuation 的范型參數,它就是 suspend 方法的返回值類型
      Type responseType = Utils.getParameterLowerBound(0,
          (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      // 如果 Continuation 的范型參數是 Response,則說明它需要的是 Response,那么將 continuationWantsResponse 置為 true;
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
          // TODO figure out if type is nullable or not
         // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
        // Find the entry for method
       // Determine if return type is nullable or not
      }
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
        // 否則獲取方法返回值的范型參數,即為請求需要的返回值的類型
      adapterType = method.getGenericReturnType();
    }
    // 通過 createCallAdapter 方法創建 CallAdapter 對象
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
    // 通過 createResponseConverter 方法創建 Converter對象
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
        // 不是suspend方法的話則直接創建并返回一個 CallAdapted 對象
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
  }

這里的代碼非常非常長,大致可歸納為下面的步驟:

1. 如果這個方法是 Kotlin 中的 suspend 方法,由于由協程實現,因此需要獲取 Continuation 的范型參數,這個參數就是請求返回值的真正類型。
2. 如果 suspend 方法返回值是 Response,則說明它需要的是 Response 而不是具體的類型,那么將 continuationWantsResponse 置為 true;
3. 如果不是 suspend 方法,則返回值的范型參數的類型就是請求返回值的真正類型(Call&lt;ReturnType&gt;ReturnType 才是真正經過轉換后需要的類型)。
4. 通過 createCallAdapter 方法創建 CallAdapter 對象,它是用于將 Call&lt;ResponseT&gt; 對象適配為需要的類型 ReturnT 對象的。
5. 拿到 CallAdapter 后,獲取到了 Response 的類型,并進行了校驗。
6. 通過 createResponseConverter 方法獲取 Converter 對象,它可以完成從 ResponseBodyResponse 類型 ResponseT 的轉換。
7. 如果并非 Kotlin 的 suspend 方法,則直接傳入 CallAdapter 及 Converter,創建 CallAdapted 對象。
8. 否則根據 suspend 方法需要的是 Response 還是具體的類型,分別返回 SuspendForResponseSuspendForBody 對象。

可以發現,新版的 Retrofit 對 Kotlin 的協程進行了支持。HttpServiceMethod.parseAnnotations 的主要作用就是創建 CallAdapter 以及 Converter 對象,并構建對應 HttpServiceMethod

CallAdapter

CallAdapter 是用于將Call&lt;R&gt;對象適配為需要的類型 T 對象的。它的聲明如下:

  public interface CallAdapter<R, T> {

      // 返回 Response 的類型
    Type responseType();
       // 將 Call<R> 轉換為 T 類型 
    T adapt(Call<R> call);
  }

我們先看看 createCallAdapter 方法是如何對它創建的:

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

它調用了 retrofit.callAdapter 方法:

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

之后調用到 retrofit.nextCallAdapter 方法:

  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
         // 遍歷 callAdapterFactories,嘗試創建 CallAdapter
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    // ...不存在對應的 CallAdapterFactory,拋出異常
  }

這里實際上是遍歷了創建 Retrofit 對象時傳遞的 CallAdapter.Factory 列表嘗試去創建 CallAdapter。如果這些 CallAdapter.Factory 都無法處理這個對應的 returnType 以及 annotations 的話,則會拋出異常。(前面 Factory 的優先級更高)

Retrofit 中有一個默認的 CallAdapter 工廠 DefaultCallAdapterFactory它的優先級比所有自定義工廠要低,它在創建時會傳入一個 Executor,我們可以看到它的 get 方法:

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

可以看到,在沒有 Executor 時,它不對 Call 進行修改,在有指定 Executor 時,則會將其包裝為 ExecutorCallbackCall。一般來說這個 Executor 就是創建 Retrofit 時指定的 callbackExecutor

這個 callbackExecutor 實際上是用來指定調用 Callback 的線程的,從而使得 Callback 并不一定是在主線程被回調:

  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");
      // 對 Callback 進行了包裝,通過 callbackExecutor 進行回調
      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));
        }
      });
    }
    // ...
  }

可以看到,這里實際上只是對 Callback 進行了包裝,通過傳遞的 Executor 進行回調,從而對 callbackExecutor 進行支持。

Converter

接著我們看看 Converter 類,它是一個接口,用于將類型 F 的數據轉換為類型 T:

  public interface Converter<F, T> {
    @Nullable T convert(F value) throws IOException;
       // ...
  }

接著我們看看 createResponseConverter 是如何對它進行創建的:

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

轉調到了 retrofit.responseBodyConverter

  public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

轉調到了 nextResponseBodyConverter

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    Objects.requireNonNull(type, "type == null");
    Objects.requireNonNull(annotations, "annotations == null");
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
    // 沒有找到對應的 ConverterFactory 進行處理,拋出異常
  }

可以看到,這里與 CallAdapter 工廠類似,遍歷創建 Retrofit 時傳入的 Converter.Factory 列表,嘗試進行創建,如果沒有工廠能對其進行處理,拋出異常。(前面 Factory 的優先級更高)

Retrofit 中內置了兩個 Converter.Factory,分別是 BuiltInConverters 以及 OptionalConverterFactory

其中 BuiltInConverters 的優先級比所有自定義工廠要高,以避免其他工廠覆蓋它的方法,而 OptionalConverterFactory的優先級比所有自定義工廠的優先級更低。

BuiltInConverters 中實現了多個轉換器如將 ResponseBody 轉換為 Void 或 Unit,將 Object 轉換為 String 等。

OptionalConverterFactory是通過 platform 獲取到的 defaultConverterFactories,它是為了支持 Java 8 的 Optional 而實現的,Optional 是 Java 8 引入的用來解決空指針異常的類。

ServiceMethod

接著我們看看之前創建的 ServiceMethod 類,它是一個抽象類,需要子類對 invoke 方法進行實現。

  abstract class ServiceMethod<T> {
    abstract @Nullable T invoke(Object[] args);
  }

它的子類就是前面提到的 HttpServiceMethod

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

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

HttpServiceMethod 的 invoke 方法非常簡單,它構造了一個 OkHttpCall,然后通過 adapt 這個虛函數來實現對 Call 的轉換。它的子類只需要實現 adapt 從而對 Call 進行轉換即可。

它共有三個子類,首先就是并非使用協程的情況下的 CallAdapted 類,另外兩個子類則是在使用協程的情況下為了配合協程的 SuspendForResponse 以及 SuspendForBody

CallAdapted

CallAdapted 類繼承自 HttpServiceMethod 類,并通過傳遞進來的 CallAdapter 對 Call 進行了轉換。

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

SuspendForResponse 類首先根據傳遞進來的 Call 構造了一個參數為 Response&lt;ResponseT&gt;Continuation 對象然后通過 Kotlin 實現的 awaitResponse 方法將 call 的 enqueue 異步回調過程封裝為了一個 suspend 的函數。

  static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    SuspendForResponse(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<Response<ResponseT>> continuation =
          (Continuation<Response<ResponseT>>) args[args.length - 1];
      // See SuspendForBody for explanation about this try/catch.
      try {
        return KotlinExtensions.awaitResponse(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }
  }

awaitResponse 方法如下:

  suspend fun <T : Any> Call<T>.awaitResponse(): Response<T> {
    return suspendCancellableCoroutine { continuation ->
      continuation.invokeOnCancellation {
        cancel()
      }
      enqueue(object : Callback<T> {
        override fun onResponse(call: Call<T>, response: Response<T>) {
          continuation.resume(response)
        }
        override fun onFailure(call: Call<T>, t: Throwable) {
          continuation.resumeWithException(t)
        }
      })
    }
  }

可以看到,分別通過在 onResponseonFailure 中調用 continuation.resumecontinuation.resumeWithException從而對協程進行支持。

SuspendForBody

SuspendForBody 則是根據傳遞進來的 Call 構造了一個 Continuation&lt;ResponseT&gt; 對象然后通過 Kotlin 實現的 await 或 awaitNullable 方法將 call 的 enqueue 異步回調過程封裝為了一個 suspend 的函數。

  static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    private final boolean isNullable;
    SuspendForBody(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, Call<ResponseT>> callAdapter, boolean isNullable) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
      this.isNullable = isNullable;
    }
    @Override protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
      // Calls to OkHttp Call.enqueue() like those inside await and awaitNullable can sometimes
      // invoke the supplied callback with an exception before the invoking stack frame can return.
      // Coroutines will intercept the subsequent invocation of the Continuation and throw the
      // exception synchronously. A Java Proxy cannot throw checked exceptions without them being
      // in an UndeclaredThrowableException, it is intercepted and supplied to a helper which will
      // force suspension to occur so that it can be instead delivered to the continuation to
      // bypass this restriction.
      try {
        return isNullable
            ? KotlinExtensions.awaitNullable(call, continuation)
            : KotlinExtensions.await(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }
  }
Call

Call 實際上是一個接口,它提供了 executeenqueuecancel 等接口用于實現請求,當我們需要請求一個接口的時候,只需要調用其 enqueueexecute方法即可。

  public interface Call<T> extends Cloneable {

    Response<T> execute() throws IOException;

    void enqueue(Callback<T> callback);

    boolean isExecuted();

    void cancel();

    boolean isCanceled();

    Call<T> clone();

    Request request();
  }

從前面的過程中我們可以了解到,如果我們沒有傳入 CalAdapter 的話,默認情況下返回的 Call 實際上是 OkHttpCall 對象,讓我們通過它來看看 Retrofit 如何基于 OkHttp 實現的網絡請求:

enqueue

首先讓我們看看 enqueue 的代碼:

  @Override public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
    okhttp3.Call call;
    Throwable failure;
    // 加鎖,對狀態進行設置并通過 createRawCall 方法創建 okhttp3.Call
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    // 如果外界取消該任務,則調用 okhttp3.Call.cancel
    if (canceled) {
      call.cancel();
    }
    // 通過 okhttp3.Call.enqueue 將消息入隊
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
          try {
            // 獲得結果后通過 parseResponse 進行解析
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }
        try {
            // 解析完成后通過 onResponse 進行回調
          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 回調失敗請求
        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
        }
      }
    });
  }

enqueue 的代碼看似多,實際上比較簡單,主要分為以下幾步:

1. 加鎖,對執行狀態進行設置,若不存在 rawCall 則調用 createRawCall 方法創建 okhttp3.Call 對象。
2. 如果外界取消該任務,則調用 okhttp3.Call.cancel
3. 通過 okhttp3.Call.enqueue 將消息入隊
4. 若獲得 Response,則通過 parseResponse 方法對 Response 進行解析,解析完成后通過 onResponse 回調解析結果。
5. 若請求失敗,通過 callFailure 方法調用 onFailure 回調請求失敗。

可以發現一個小細節,Retrofit 對已經創建的 okhttp3.Call 進行了復用,避免了重復創建從而浪費效率。

execute

接著讓我們看看 execute 是如何實現的:

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }
      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());
  }

也非常簡單:

1.首先加鎖后對執行狀態進行設置,若不存在 rawCall 則調用 createRawCall方法創建 okhttp3.Call 對象。
2.如果外界取消該任務,則調用 okhttp3.Call.cancel
3.若獲得 Response,則通過 parseResponse 方法對 Response 進行解析并返回

okhttp3.Call 的創建

接著讓我們看看 createRawCall 方法:

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

它實際上是調用了 callFactorynewCall 方法進行創建,而傳入的 okhttp3.Request 則是通過 requestFactory.create 創建的:

  okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    int argumentCount = args.length;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount+ ") doesn't match expected count (" + handlers.length + ")");
    }
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart);
    if (isKotlinSuspendFunction) {
      // The Continuation is the last parameter and the handlers array contains null at that index.
      argumentCount--;
    }
    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }
    return requestBuilder.get(.tag(Invocation.class, new Invocation(method, argumentList)) .build();
  }

這里首先構建了一個 RequestBuilder,之后通過遍歷 ParamterHandler 列表并調用其 apply 方法將參數應用到 RequestBuilder 中。

parseResponse

接著我們看看 parseResponse 是如何對 Response 進行解析的:

  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();
    // ...
    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;
    }
  }

可以看到,它會通過 Converter.convert 方法將 Response 的 body 轉換為我們所需要的類型。

總結

那么到這里,Retrofit的源碼我們就基本上看完了,可以發現它的代碼中基本很少涉及到對網絡請求的處理,基本都是對于 OkHttp 的封裝,在 OkHttp 現有的 API 上提供了一套更便于使用的框架。

Retrofit 很好地向我們證明了,如何在一套已經固定的 API 上,以最優雅的方式對現有的框架進行封裝,拓展出更為強大的功能。

Retrofit 的注解是一種運行時注解,它通過動態代理對 Service 對象進行創建,通過反射對注解進行解析,這樣雖然會有一定性能的損耗,但性能的損耗卻帶來了十分易用的 API。用戶所寫的 Service 接口中,每一個 ServiceMethod都對應了一個接口。

對于 Service 的創建,它是通過動態代理,對每個接口內定義的方法進行了代理,若設置 validateEagerly了則會在創建 Service 接口對象時進行注解的解析以及 ServiceMethod 的創建,否則會在方法調用時再創建對應的 ServiceMethod 對象,在多次調用的情況下,它通過 serviceMethodCache 對已經解析的 ServiceMethod 進行了緩存從而避免了重復解析帶來的性能損耗。

這個對象的創建首先會經過 RequestFactory.parseAnnotations 對方法中的注解進行解析:

  • 對于方法的請求方式注解,它會通過 parseHttpMethodAndPath 方法獲取注解對應的請求方式以及注解中的 url 等信息并保存起來,在創建請求的時候設置進 RequestBuilder

  • 對于方法的 Headers 注解,它會將 Header 注解解析為 Headers 對象并保存起來,在創建請求的時候設置進 RequestBuilder

  • 對于方法的參數,它會將每個參數根據具體的注解(@Query 等)解析為對應的 ParamterHandler,在創建請求的時候,會通過它的 apply 方法將參數提交到 RequestBuilder 中。

之后,這個對象會通過 HttpServiceMethod.parseAnnotationsServiceMethod 對象進行創建,它在創建的過程中同時進行了接口對應的 CallAdapter 以及 Converter 的創建。

其中,CallAdapter 用于將Call&lt;R&gt; 對象適配為需要的類型 T 對象,也就是對 Call 進行轉換。

Converter 則是用于將 F 類型的數據轉換為 T,往往是用于對 Response的 body 進行轉換。

對于 CallAdapterConverter 都是通過它們對應的工廠類進行創建,創建時會根據工廠列表的順序從前向后嘗試進行創建,也就是說在工廠列表中越靠前的工廠其優先級越大。

同時,Retrofit 還引入了對 Continuation 協程的支持,它會將 ServerMethod 最終包裝為一個 suspend 方法從而對協程進行支持。

Retrofit的網絡請求的執行依賴于 OkHttp,它首先會通過 RequestFactory 進行 Request 的構造,它的參數通過前面解析的信息得來。之后會將這個 Request 包裝為一個 okhttp3.Call,在同步和異步請求時分別調用其對應的 execute 及 enqueue 方法。同時,為了避免okhttp3.Call 的重復創建,它對之前創建的 okhttp3.Call 進行了復用。

以上是“OkHttp與Retrofit的區別有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临潭县| 昭平县| 五常市| 新乐市| 徐州市| 徐汇区| 安西县| 海丰县| 曲阳县| 资阳市| 鄂伦春自治旗| 漾濞| 黄浦区| 万载县| 泾源县| 泰兴市| 南召县| 黄平县| 宁蒗| 买车| 特克斯县| 汾西县| 绩溪县| 英德市| 九龙县| 泰顺县| 葫芦岛市| 包头市| 离岛区| 达尔| 靖边县| 随州市| 孝感市| 汽车| 桂东县| 安仁县| 土默特左旗| 乌拉特前旗| 哈密市| 龙岩市| 宣威市|