Retrofit2.3.0 源碼解析

Retrofit是對OKHttp的封裝,簡化了網(wǎng)絡請求伴郁。具體使用參見官方文檔耿战。本文分析的代碼是 retrofit2.3.0

先看一下retrofit官方的實例:

public final class SimpleService {
  public static final String API_URL = "https://api.github.com";

  public static class Contributor {
    public final String login;
    public final int contributions;

    public Contributor(String login, int contributions) {
      this.login = login;
      this.contributions = contributions;
    }
  }
  // 1
  public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(
        @Path("owner") String owner,
        @Path("repo") String repo);
  }

  public static void main(String... args) throws IOException {
    // Create a very simple REST adapter which points the GitHub API.
    //2
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

    // Create an instance of our GitHub API interface.
    //3
    GitHub github = retrofit.create(GitHub.class);

    // Create a call instance for looking up Retrofit contributors.
    //4
    Call<List<Contributor>> call = github.contributors("square", "retrofit");

    // Fetch and print a list of the contributors to the library.
    //5
    List<Contributor> contributors = call.execute().body();
    //6
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}

其中Retrofit的步驟:

  1. 創(chuàng)建一個接口(GitHub 接口)。
  2. 創(chuàng)建一個Retrofit對象焊傅,并提供baseUrl剂陡,ConverterFactory等。
  3. 創(chuàng)建一個實現(xiàn)接口的一個代理對象狐胎。
  4. 調(diào)用接口方法并返回一個Call對象鸭栖。
  5. 執(zhí)行exceute()同步請求。
  6. 解析Response握巢。

接下來我們以這個步驟一步一步來看晕鹊。

1、接口

retrofit2.http包暴浦,里面全部是定義HTTP請求的Java注解溅话,比如GETPOST歌焦、PUT飞几、DELETEHeaders独撇、Path循狰、Query等等。所以在接口里用注解方式定義請求類型等券勺。

2绪钥、Retrofit對象

Retrofit 對象的創(chuàng)建還是用目前流行的Build模式創(chuàng)建。

public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }
    ....

    /**
     * Create the {@link Retrofit} instance using the configured values.
     * <p>
     * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
     * OkHttpClient} will be created and used.
     */
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        //1
        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.
      // 2
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

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

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  }
  1. 通過build() 創(chuàng)建 OkHttpClient對象关炼。
  2. 設置一個CallAdapterFactory.
  3. 添加各種Converter 轉換工廠程腹。

這里的AdapterFactory 默認使用DefaultCallAdapterFactory

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }

    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
    };
  }
}

這個DefaultCallAdapterFactory 下面會用到。

3儒拂、retrofit.create() 返回4 中的 Call對象

通過retrofit.create 高镐,動態(tài)創(chuàng)建一個接口代理對象。

public <T> T create(final Class<T> service) {
    //判斷是否是接口.
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }

    //Proxy.newProxyInstance  返回一個service 的代理對象舞萄。
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            //如果這個方法是對象里面的水醋,則直接調(diào)用
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }


            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

            //創(chuàng)建OkHttpCall
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

這里通過Proxy.newProxyInstance 返回一個service 的代理對象。這里涉及到java動態(tài)代理知識蒜哀,不熟悉的同學先去了解動態(tài)代理斩箫。

當我們調(diào)用這個代理的方法時,比如調(diào)用contributors() 方法時會調(diào)用invoke()方法。

在這個代理里面先通過loadServiceMethod() 獲取到ServiceMethod 乘客,在通過ServiceMethod 創(chuàng)建OkHttpCall狐血,然后調(diào)用CallAdapter 的 adapt 返回一個實體T。

接下去看 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); 通過loadServiceMethod() 獲取到ServiceMethod易核。

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //構建ServiceMethod
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

ServiceMethod 的構建還是用到build模式匈织。

public ServiceMethod build() {

      //1
      callAdapter = createCallAdapter();

      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }

      //2
      responseConverter = createResponseConverter();
      // 3
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
      //4
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }
  1. 創(chuàng)建 CallAdapter
  2. 創(chuàng)建轉換器,將返回的結果進行轉換牡直。
  3. 處理請求的Annotation 注解缀匕。
  4. 處理參數(shù)的注解。

看一下createCallAdapter()

private CallAdapter<T, R> createCallAdapter() {
      //獲取Type
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      //獲取 目標方法的Annotation
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        //獲取  CallAdapter
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

這里又調(diào)到retrofit中的callAdapter()方法碰逸。

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
        //從 adapterFactories中獲取CallAdapter
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
        .append(returnType)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

nextCallAdapter中 通過adapterFactories集合獲取到CallAdapter弦追,而默認的adapterFactories中是我們在Retrofit.build中添加的一個默認CallAdapterFactory也就是上面說的DefaultCallAdapterFactory

最后在create中return serviceMethod.callAdapter.adapt(okHttpCall); 也就是調(diào)用DefaultCallAdapterFactory 中的adapt().

 @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }

直接返回傳進來的Call也就是OkHttpCall

5花竞、exceute()

接下來執(zhí)行Call.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 {
          //1
          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();
    }
    //2
    return parseResponse(call.execute());
  }
  1. 真正的Call是通過createRawCall() 實現(xiàn)。
  2. 將返回的Response進行包裝约急。
private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);

    //1
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
  1. 這里調(diào)用 OkHttp的 newCall()零远,開始進入OkHttp的網(wǎng)絡請求。關于OkHttp的源碼分析厌蔽,看這里

    ?

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

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(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;
    }
  }

這里講OkHttp返回的Response通過Converter轉換器包裝成我們定義的Response<T> 類型返回回去牵辣。

總結

Retrofit就是對OkHttp 進行封裝,Retrofit本身沒有提供網(wǎng)絡訪問的能力奴饮,也是通過OkHttp去實現(xiàn)的纬向。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市戴卜,隨后出現(xiàn)的幾起案子逾条,更是在濱河造成了極大的恐慌,老刑警劉巖投剥,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件师脂,死亡現(xiàn)場離奇詭異,居然都是意外死亡江锨,警方通過查閱死者的電腦和手機吃警,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啄育,“玉大人酌心,你說我怎么就攤上這事√敉悖” “怎么了安券?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵墩崩,是天一觀的道長。 經(jīng)常有香客問我完疫,道長,這世上最難降的妖魔是什么债蓝? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任壳鹤,我火速辦了婚禮,結果婚禮上饰迹,老公的妹妹穿的比我還像新娘芳誓。我一直安慰自己,他們只是感情好啊鸭,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布锹淌。 她就那樣靜靜地躺著,像睡著了一般赠制。 火紅的嫁衣襯著肌膚如雪赂摆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天钟些,我揣著相機與錄音烟号,去河邊找鬼。 笑死政恍,一個胖子當著我的面吹牛汪拥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篙耗,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼迫筑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宗弯?” 一聲冷哼從身側響起脯燃,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒙保,沒想到半個月后曲伊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡追他,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年坟募,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邑狸。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡懈糯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出单雾,到底是詐尸還是另有隱情赚哗,我是刑警寧澤她紫,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站屿储,受9級特大地震影響贿讹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜够掠,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一民褂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疯潭,春花似錦赊堪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至相叁,卻和暖如春遵绰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背增淹。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工街立, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人埠通。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓赎离,卻偏偏與公主長得像,于是被迫代替她去往敵國和親端辱。 傳聞我的和親對象是個殘疾皇子梁剔,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容